fold-agent 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 (74) hide show
  1. package/README.md +14 -0
  2. package/bin/fold-agent.js +2 -0
  3. package/dist/cli/app.d.ts +4 -0
  4. package/dist/cli/app.js +1113 -0
  5. package/dist/cli/app.js.map +1 -0
  6. package/dist/cli/bin.d.ts +2 -0
  7. package/dist/cli/bin.js +8 -0
  8. package/dist/cli/bin.js.map +1 -0
  9. package/dist/cli/context.d.ts +5 -0
  10. package/dist/cli/context.js +2 -0
  11. package/dist/cli/context.js.map +1 -0
  12. package/dist/cli/operations.d.ts +126 -0
  13. package/dist/cli/operations.js +1159 -0
  14. package/dist/cli/operations.js.map +1 -0
  15. package/dist/cli/output.d.ts +22 -0
  16. package/dist/cli/output.js +245 -0
  17. package/dist/cli/output.js.map +1 -0
  18. package/dist/cli/package-info.d.ts +5 -0
  19. package/dist/cli/package-info.js +12 -0
  20. package/dist/cli/package-info.js.map +1 -0
  21. package/dist/cli/results.d.ts +393 -0
  22. package/dist/cli/results.js +2 -0
  23. package/dist/cli/results.js.map +1 -0
  24. package/dist/cli/skill-install.d.ts +7 -0
  25. package/dist/cli/skill-install.js +211 -0
  26. package/dist/cli/skill-install.js.map +1 -0
  27. package/dist/deploy/public-origin.d.ts +15 -0
  28. package/dist/deploy/public-origin.js +59 -0
  29. package/dist/deploy/public-origin.js.map +1 -0
  30. package/dist/rooms/append-log-api.d.ts +16 -0
  31. package/dist/rooms/append-log-api.js +72 -0
  32. package/dist/rooms/append-log-api.js.map +1 -0
  33. package/dist/rooms/append-log-validation.d.ts +2 -0
  34. package/dist/rooms/append-log-validation.js +16 -0
  35. package/dist/rooms/append-log-validation.js.map +1 -0
  36. package/dist/rooms/comments.d.ts +63 -0
  37. package/dist/rooms/comments.js +136 -0
  38. package/dist/rooms/comments.js.map +1 -0
  39. package/dist/rooms/crypto.d.ts +11 -0
  40. package/dist/rooms/crypto.js +44 -0
  41. package/dist/rooms/crypto.js.map +1 -0
  42. package/dist/rooms/encrypted-records.d.ts +5 -0
  43. package/dist/rooms/encrypted-records.js +21 -0
  44. package/dist/rooms/encrypted-records.js.map +1 -0
  45. package/dist/rooms/markdown-snapshot.d.ts +23 -0
  46. package/dist/rooms/markdown-snapshot.js +126 -0
  47. package/dist/rooms/markdown-snapshot.js.map +1 -0
  48. package/dist/rooms/metadata.d.ts +32 -0
  49. package/dist/rooms/metadata.js +118 -0
  50. package/dist/rooms/metadata.js.map +1 -0
  51. package/dist/rooms/personas.d.ts +16 -0
  52. package/dist/rooms/personas.js +78 -0
  53. package/dist/rooms/personas.js.map +1 -0
  54. package/dist/rooms/project-state.d.ts +41 -0
  55. package/dist/rooms/project-state.js +249 -0
  56. package/dist/rooms/project-state.js.map +1 -0
  57. package/dist/rooms/proposals.d.ts +63 -0
  58. package/dist/rooms/proposals.js +254 -0
  59. package/dist/rooms/proposals.js.map +1 -0
  60. package/dist/rooms/replay.d.ts +13 -0
  61. package/dist/rooms/replay.js +19 -0
  62. package/dist/rooms/replay.js.map +1 -0
  63. package/dist/rooms/room-reference.d.ts +21 -0
  64. package/dist/rooms/room-reference.js +142 -0
  65. package/dist/rooms/room-reference.js.map +1 -0
  66. package/dist/rooms/timeline.d.ts +26 -0
  67. package/dist/rooms/timeline.js +68 -0
  68. package/dist/rooms/timeline.js.map +1 -0
  69. package/package.json +35 -0
  70. package/skills/fold/SKILL.md +81 -0
  71. package/skills/fold/agents/openai.yaml +4 -0
  72. package/skills/fold/references/cli.md +33 -0
  73. package/skills/fold/references/security.md +14 -0
  74. package/skills/fold/references/workflow.md +48 -0
@@ -0,0 +1,254 @@
1
+ import { randomUUID } from 'node:crypto';
2
+ import { summarizeMarkdown } from './markdown-snapshot.js';
3
+ import { assignPersona } from './personas.js';
4
+ import { normalizeProjectSnapshot, summarizeProject, } from './project-state.js';
5
+ import { createTimelineEvent, decryptTimelineEvent, TIMELINE_EVENT_SENDER_ID_PREFIX, } from './timeline.js';
6
+ import { decryptJsonRecord, encryptJsonRecord } from './encrypted-records.js';
7
+ import { CLI_COMMENT_EVENT_SENDER_ID_PREFIX, COMMENT_EVENT_SENDER_ID_PREFIX, } from './comments.js';
8
+ import { assertContiguousRecords } from './append-log-validation.js';
9
+ export const PROPOSAL_SCHEMA = 'fold.proposal.v1';
10
+ export const PROPOSAL_SENDER_ID_PREFIX = 'fold-cli:proposal';
11
+ export const DEFAULT_AGENT_PARTICIPANT_FINGERPRINT = 'fold-cli:proposal';
12
+ export async function createEncryptedProposalRecord(options) {
13
+ const createdAt = new Date().toISOString();
14
+ const id = randomUUID();
15
+ const base = summarizeMarkdown(options.baseMarkdown);
16
+ const proposed = summarizeMarkdown(options.proposedMarkdown);
17
+ const proposedProject = options.proposedProject ? normalizeProjectSnapshot(options.proposedProject) : undefined;
18
+ const baseProject = options.baseProject ? summarizeProject(options.baseProject) : undefined;
19
+ const persona = assignPersona({
20
+ roomId: options.access.roomId,
21
+ participantKind: 'agent',
22
+ participantFingerprint: options.participantFingerprint ?? DEFAULT_AGENT_PARTICIPANT_FINGERPRINT,
23
+ });
24
+ const record = {
25
+ schema: PROPOSAL_SCHEMA,
26
+ id,
27
+ kind: options.path
28
+ ? 'file-replacement'
29
+ : proposedProject
30
+ ? 'project-replacement'
31
+ : 'whole-document-replacement',
32
+ createdAt,
33
+ updatedAt: createdAt,
34
+ title: options.title ?? defaultProposalTitle(options.baseMarkdown, options.proposedMarkdown),
35
+ comment: options.comment ?? '',
36
+ authorKind: 'agent',
37
+ authorPersonaId: persona.id,
38
+ persona,
39
+ baseVersionId: base.sha256,
40
+ base,
41
+ proposed: {
42
+ ...proposed,
43
+ markdown: options.proposedMarkdown,
44
+ },
45
+ proposedMarkdown: options.proposedMarkdown,
46
+ baseProject,
47
+ proposedProject,
48
+ path: options.path,
49
+ diff: proposedProject && options.baseProject
50
+ ? projectDiff(options.baseProject, proposedProject)
51
+ : wholeDocumentDiff(options.baseMarkdown, options.proposedMarkdown),
52
+ discussionThreadIds: [],
53
+ };
54
+ const timelineEvent = createTimelineEvent({
55
+ idSeed: `proposal:${id}`,
56
+ type: 'proposal_submitted',
57
+ createdAt,
58
+ actorPersonaId: persona.id,
59
+ proposalId: id,
60
+ documentSha256: record.proposed.sha256,
61
+ message: record.title,
62
+ });
63
+ const proposal = {
64
+ ...record,
65
+ status: 'pending',
66
+ statusUpdatedAt: createdAt,
67
+ };
68
+ return {
69
+ update: await encryptJsonRecord(options.access, `${PROPOSAL_SENDER_ID_PREFIX}:${randomUUID()}`, record),
70
+ proposal,
71
+ timelineEvent,
72
+ };
73
+ }
74
+ export async function createProposalAcceptedEvent(access, proposal, documentSha256, actorPersonaId, acceptedProject) {
75
+ const event = createTimelineEvent({
76
+ idSeed: `proposal:${proposal.id}:accepted`,
77
+ type: 'proposal_accepted',
78
+ actorPersonaId,
79
+ proposalId: proposal.id,
80
+ documentSha256,
81
+ message: `Accepted ${proposal.title}`,
82
+ acceptedProject: acceptedProject ?? proposal.proposedProject,
83
+ });
84
+ return encryptJsonRecord(access, `${TIMELINE_EVENT_SENDER_ID_PREFIX}:${event.id}`, event);
85
+ }
86
+ export async function createProposalRejectedEvent(access, proposal, actorPersonaId, reason) {
87
+ const event = createTimelineEvent({
88
+ idSeed: `proposal:${proposal.id}:rejected`,
89
+ type: 'proposal_rejected',
90
+ actorPersonaId,
91
+ proposalId: proposal.id,
92
+ documentSha256: null,
93
+ message: reason ? `Rejected ${proposal.title}: ${reason}` : `Rejected ${proposal.title}`,
94
+ });
95
+ return encryptJsonRecord(access, `${TIMELINE_EVENT_SENDER_ID_PREFIX}:${event.id}`, event);
96
+ }
97
+ export async function replayProposalsFromRecords(access, records) {
98
+ assertContiguousRecords(records, access.roomId);
99
+ const proposals = new Map();
100
+ const timeline = [];
101
+ for (const record of records) {
102
+ if (record.senderId.startsWith(PROPOSAL_SENDER_ID_PREFIX)) {
103
+ const proposal = await decryptProposalRecord(access, record, record.senderId);
104
+ if (proposals.has(proposal.id))
105
+ continue;
106
+ proposals.set(proposal.id, {
107
+ ...proposal,
108
+ status: 'pending',
109
+ statusUpdatedAt: proposal.updatedAt,
110
+ replies: [],
111
+ });
112
+ continue;
113
+ }
114
+ if (isProposalDiscussionEventSender(record.senderId)) {
115
+ const event = await decryptJsonRecord(access, record, record.senderId);
116
+ if (!isProposalReplyEvent(event))
117
+ continue;
118
+ const current = proposals.get(event.proposalId);
119
+ if (!current)
120
+ continue;
121
+ const replies = current.replies || [];
122
+ if (replies.some((reply) => reply.id === event.reply.id))
123
+ continue;
124
+ proposals.set(current.id, {
125
+ ...current,
126
+ replies: [...replies, event.reply].sort((left, right) => left.createdAt.localeCompare(right.createdAt)),
127
+ });
128
+ continue;
129
+ }
130
+ if (!record.senderId.startsWith(TIMELINE_EVENT_SENDER_ID_PREFIX))
131
+ continue;
132
+ const event = await decryptTimelineEvent(access, record, record.senderId);
133
+ timeline.push(event);
134
+ if (!event.proposalId)
135
+ continue;
136
+ const current = proposals.get(event.proposalId);
137
+ if (!current || current.status !== 'pending')
138
+ continue;
139
+ if (event.type === 'proposal_accepted') {
140
+ proposals.set(current.id, {
141
+ ...current,
142
+ status: 'accepted',
143
+ statusUpdatedAt: event.createdAt,
144
+ });
145
+ }
146
+ else if (event.type === 'proposal_rejected') {
147
+ proposals.set(current.id, {
148
+ ...current,
149
+ status: 'rejected',
150
+ statusUpdatedAt: event.createdAt,
151
+ });
152
+ }
153
+ }
154
+ return {
155
+ proposals: [...proposals.values()].sort((left, right) => left.createdAt.localeCompare(right.createdAt)),
156
+ timeline: timeline.sort((left, right) => left.createdAt.localeCompare(right.createdAt)),
157
+ };
158
+ }
159
+ function isProposalDiscussionEventSender(senderId) {
160
+ return senderId.startsWith(COMMENT_EVENT_SENDER_ID_PREFIX) || senderId.startsWith(CLI_COMMENT_EVENT_SENDER_ID_PREFIX);
161
+ }
162
+ function isProposalReplyEvent(value) {
163
+ if (!value || typeof value !== 'object')
164
+ return false;
165
+ const candidate = value;
166
+ const reply = candidate.reply;
167
+ return candidate.type === 'proposal_replied'
168
+ && typeof candidate.proposalId === 'string'
169
+ && Boolean(reply)
170
+ && typeof reply?.id === 'string'
171
+ && typeof reply.authorPersonaId === 'string'
172
+ && reply.persona?.schema === 'fold.persona.v1'
173
+ && typeof reply.text === 'string'
174
+ && typeof reply.createdAt === 'string';
175
+ }
176
+ export async function decryptProposalRecord(access, payload, senderId) {
177
+ const value = await decryptJsonRecord(access, payload, senderId);
178
+ if (!isProposalRecord(value)) {
179
+ throw new Error('Invalid encrypted proposal payload');
180
+ }
181
+ return value;
182
+ }
183
+ function wholeDocumentDiff(baseMarkdown, proposedMarkdown) {
184
+ if (baseMarkdown === proposedMarkdown)
185
+ return '';
186
+ return [
187
+ '--- current.md',
188
+ '+++ proposed.md',
189
+ '@@ whole-document-replacement @@',
190
+ ...baseMarkdown.split('\n').map((line) => `-${line}`),
191
+ ...proposedMarkdown.split('\n').map((line) => `+${line}`),
192
+ ].join('\n');
193
+ }
194
+ function defaultProposalTitle(baseMarkdown, proposedMarkdown) {
195
+ const baseLines = lineCount(baseMarkdown);
196
+ const proposedLines = lineCount(proposedMarkdown);
197
+ return `Whole-document proposal (${baseLines} lines -> ${proposedLines} lines)`;
198
+ }
199
+ function lineCount(markdown) {
200
+ if (!markdown)
201
+ return 0;
202
+ return markdown.split('\n').length;
203
+ }
204
+ function isProposalRecord(value) {
205
+ if (!value || typeof value !== 'object')
206
+ return false;
207
+ const candidate = value;
208
+ return (candidate.schema === PROPOSAL_SCHEMA &&
209
+ (candidate.kind === 'whole-document-replacement' ||
210
+ candidate.kind === 'file-replacement' ||
211
+ candidate.kind === 'project-replacement') &&
212
+ typeof candidate.id === 'string' &&
213
+ typeof candidate.createdAt === 'string' &&
214
+ typeof candidate.updatedAt === 'string' &&
215
+ typeof candidate.title === 'string' &&
216
+ typeof candidate.comment === 'string' &&
217
+ candidate.authorKind === 'agent' &&
218
+ typeof candidate.authorPersonaId === 'string' &&
219
+ Boolean(candidate.persona) &&
220
+ typeof candidate.persona?.id === 'string' &&
221
+ typeof candidate.baseVersionId === 'string' &&
222
+ Boolean(candidate.base) &&
223
+ typeof candidate.base?.sha256 === 'string' &&
224
+ Boolean(candidate.proposed) &&
225
+ typeof candidate.proposed?.sha256 === 'string' &&
226
+ typeof candidate.proposed?.markdown === 'string' &&
227
+ typeof candidate.proposedMarkdown === 'string' &&
228
+ (candidate.path === undefined || typeof candidate.path === 'string') &&
229
+ (candidate.baseProject === undefined || typeof candidate.baseProject.sha256 === 'string') &&
230
+ (candidate.proposedProject === undefined || Array.isArray(candidate.proposedProject.files)) &&
231
+ typeof candidate.diff === 'string' &&
232
+ Array.isArray(candidate.discussionThreadIds));
233
+ }
234
+ function projectDiff(baseProject, proposedProject) {
235
+ const base = new Map(normalizeProjectSnapshot(baseProject).files.map((file) => [file.path, file.markdown]));
236
+ const proposed = new Map(normalizeProjectSnapshot(proposedProject).files.map((file) => [file.path, file.markdown]));
237
+ const paths = [...new Set([...base.keys(), ...proposed.keys()])].sort();
238
+ const lines = [];
239
+ for (const path of paths) {
240
+ const before = base.get(path);
241
+ const after = proposed.get(path);
242
+ if (before === after)
243
+ continue;
244
+ lines.push(`--- ${path}`);
245
+ lines.push(`+++ ${path}`);
246
+ lines.push('@@ file-replacement @@');
247
+ if (before !== undefined)
248
+ lines.push(...before.split('\n').map((line) => `-${line}`));
249
+ if (after !== undefined)
250
+ lines.push(...after.split('\n').map((line) => `+${line}`));
251
+ }
252
+ return lines.join('\n');
253
+ }
254
+ //# sourceMappingURL=proposals.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"proposals.js","sourceRoot":"","sources":["../../../../src/rooms/proposals.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,13 @@
1
+ import type { EncryptedUpdateRecord } from '../server/append-log.js';
2
+ import { type RoomComment } from './comments.js';
3
+ import { type ProjectSnapshot } from './project-state.js';
4
+ import { type ProposalReplay } from './proposals.js';
5
+ import type { RoomAccess } from './room-reference.js';
6
+ import { type TimelineEvent } from './timeline.js';
7
+ export interface RoomReplay {
8
+ projectSnapshots: ProjectSnapshot[];
9
+ proposals: ProposalReplay;
10
+ comments: RoomComment[];
11
+ timeline: TimelineEvent[];
12
+ }
13
+ export declare function replayRoomRecords(access: RoomAccess, records: EncryptedUpdateRecord[]): Promise<RoomReplay>;
@@ -0,0 +1,19 @@
1
+ import { replayCommentsFromRecords } from './comments.js';
2
+ import { decryptProjectSnapshotsFromRecords } from './project-state.js';
3
+ import { replayProposalsFromRecords } from './proposals.js';
4
+ import { decryptTimelineEventsFromRecords } from './timeline.js';
5
+ export async function replayRoomRecords(access, records) {
6
+ const [projectSnapshots, proposals, comments, timeline] = await Promise.all([
7
+ decryptProjectSnapshotsFromRecords(access, records),
8
+ replayProposalsFromRecords(access, records),
9
+ replayCommentsFromRecords(access, records),
10
+ decryptTimelineEventsFromRecords(access, records),
11
+ ]);
12
+ return {
13
+ projectSnapshots,
14
+ proposals,
15
+ comments,
16
+ timeline,
17
+ };
18
+ }
19
+ //# sourceMappingURL=replay.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"replay.js","sourceRoot":"","sources":["../../../../src/rooms/replay.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,yBAAyB,EAAoB,MAAM,eAAe,CAAC;AAC5E,OAAO,EAAE,kCAAkC,EAAwB,MAAM,oBAAoB,CAAC;AAC9F,OAAO,EAAE,0BAA0B,EAAuB,MAAM,gBAAgB,CAAC;AAEjF,OAAO,EAAE,gCAAgC,EAAsB,MAAM,eAAe,CAAC;AASrF,MAAM,CAAC,KAAK,UAAU,iBAAiB,CACrC,MAAkB,EAClB,OAAgC;IAEhC,MAAM,CAAC,gBAAgB,EAAE,SAAS,EAAE,QAAQ,EAAE,QAAQ,CAAC,GAAG,MAAM,OAAO,CAAC,GAAG,CAAC;QAC1E,kCAAkC,CAAC,MAAM,EAAE,OAAO,CAAC;QACnD,0BAA0B,CAAC,MAAM,EAAE,OAAO,CAAC;QAC3C,yBAAyB,CAAC,MAAM,EAAE,OAAO,CAAC;QAC1C,gCAAgC,CAAC,MAAM,EAAE,OAAO,CAAC;KAClD,CAAC,CAAC;IAEH,OAAO;QACL,gBAAgB;QAChB,SAAS;QACT,QAAQ;QACR,QAAQ;KACT,CAAC;AACJ,CAAC"}
@@ -0,0 +1,21 @@
1
+ export declare const ROOM_TOKEN_PREFIX = "fold:v1:";
2
+ export declare const DEFAULT_SERVER_URL = "http://localhost:8787";
3
+ export interface RoomAccess {
4
+ roomId: string;
5
+ roomSecret: string;
6
+ appUrl: string;
7
+ syncUrl: string;
8
+ serverUrl: string;
9
+ }
10
+ export interface ParsedRoomReference extends RoomAccess {
11
+ kind: 'token' | 'url';
12
+ roomUrl: string;
13
+ serverRoomUrl: string;
14
+ }
15
+ export declare function createRoomAccess(serverUrl?: string, appUrl?: string, syncUrl?: string): RoomAccess;
16
+ export declare function createRoomToken(access: RoomAccess): string;
17
+ export declare function parseRoomReference(input: string): ParsedRoomReference;
18
+ export declare function roomUrlForAccess(access: RoomAccess): string;
19
+ export declare function serverRoomUrlForAccess(access: RoomAccess): string;
20
+ export declare function appRoomUrlForAccess(access: RoomAccess): string;
21
+ export declare function normalizeServerUrl(input: string): string;
@@ -0,0 +1,142 @@
1
+ import { webcrypto } from 'node:crypto';
2
+ export const ROOM_TOKEN_PREFIX = 'fold:v1:';
3
+ export const DEFAULT_SERVER_URL = 'http://localhost:8787';
4
+ export function createRoomAccess(serverUrl = DEFAULT_SERVER_URL, appUrl = serverUrl, syncUrl = serverUrl) {
5
+ return {
6
+ roomId: randomBase64Url(16),
7
+ roomSecret: randomBase64Url(32),
8
+ appUrl: normalizeServerUrl(appUrl),
9
+ syncUrl: normalizeServerUrl(syncUrl),
10
+ serverUrl: normalizeServerUrl(syncUrl),
11
+ };
12
+ }
13
+ export function createRoomToken(access) {
14
+ const token = {
15
+ v: 1,
16
+ roomId: access.roomId,
17
+ roomSecret: access.roomSecret,
18
+ appUrl: normalizeServerUrl(access.appUrl ?? access.serverUrl),
19
+ syncUrl: normalizeServerUrl(access.syncUrl ?? access.serverUrl),
20
+ };
21
+ return `${ROOM_TOKEN_PREFIX}${Buffer.from(JSON.stringify(token), 'utf8').toString('base64url')}`;
22
+ }
23
+ export function parseRoomReference(input) {
24
+ if (input.startsWith(ROOM_TOKEN_PREFIX)) {
25
+ return parsedFromAccess(parseRoomToken(input), 'token');
26
+ }
27
+ return parseRoomUrl(input);
28
+ }
29
+ export function roomUrlForAccess(access) {
30
+ return `${appRoomUrlForAccess(access)}#key=${encodeURIComponent(access.roomSecret)}`;
31
+ }
32
+ export function serverRoomUrlForAccess(access) {
33
+ return appRoomUrlForAccess(access);
34
+ }
35
+ export function appRoomUrlForAccess(access) {
36
+ return `${normalizeServerUrl(access.appUrl ?? access.serverUrl)}/room/${encodeURIComponent(access.roomId)}`;
37
+ }
38
+ export function normalizeServerUrl(input) {
39
+ let parsed;
40
+ try {
41
+ parsed = new URL(input);
42
+ }
43
+ catch (error) {
44
+ throw new Error(`Invalid server URL ${JSON.stringify(input)}`, { cause: error });
45
+ }
46
+ if (parsed.protocol !== 'http:' && parsed.protocol !== 'https:') {
47
+ throw new Error(`Server URL must use http or https: ${JSON.stringify(input)}`);
48
+ }
49
+ parsed.hash = '';
50
+ parsed.search = '';
51
+ parsed.pathname = parsed.pathname.replace(/\/+$/, '');
52
+ return parsed.toString().replace(/\/$/, '');
53
+ }
54
+ function parseRoomToken(input) {
55
+ const encoded = input.slice(ROOM_TOKEN_PREFIX.length);
56
+ let raw;
57
+ try {
58
+ raw = JSON.parse(Buffer.from(encoded, 'base64url').toString('utf8'));
59
+ }
60
+ catch (error) {
61
+ throw new Error('Invalid fold token encoding', { cause: error });
62
+ }
63
+ if (!isEncodedRoomToken(raw)) {
64
+ throw new Error('Invalid fold token schema');
65
+ }
66
+ return {
67
+ roomId: raw.roomId,
68
+ roomSecret: raw.roomSecret,
69
+ appUrl: normalizeServerUrl(raw.appUrl ?? raw.serverUrl ?? DEFAULT_SERVER_URL),
70
+ syncUrl: normalizeServerUrl(raw.syncUrl ?? raw.serverUrl ?? raw.appUrl ?? DEFAULT_SERVER_URL),
71
+ serverUrl: normalizeServerUrl(raw.syncUrl ?? raw.serverUrl ?? raw.appUrl ?? DEFAULT_SERVER_URL),
72
+ };
73
+ }
74
+ function parseRoomUrl(input) {
75
+ let parsed;
76
+ try {
77
+ parsed = new URL(input);
78
+ }
79
+ catch (error) {
80
+ throw new Error('Room must be a fold token or room URL', { cause: error });
81
+ }
82
+ const roomPath = roomPathFromUrl(parsed);
83
+ const fragment = new URLSearchParams(parsed.hash.startsWith('#') ? parsed.hash.slice(1) : parsed.hash);
84
+ const roomSecret = fragment.get('key');
85
+ if (!roomPath) {
86
+ throw new Error('Room URL must contain /room/:roomId');
87
+ }
88
+ if (!roomSecret) {
89
+ throw new Error('Room URL must contain client-side key material in #key=...');
90
+ }
91
+ return parsedFromAccess({
92
+ roomId: roomPath.roomId,
93
+ roomSecret,
94
+ appUrl: roomPath.serverUrl,
95
+ syncUrl: roomPath.serverUrl,
96
+ serverUrl: roomPath.serverUrl,
97
+ }, 'url');
98
+ }
99
+ function parsedFromAccess(access, kind) {
100
+ const normalized = {
101
+ ...access,
102
+ appUrl: normalizeServerUrl(access.appUrl ?? access.serverUrl),
103
+ syncUrl: normalizeServerUrl(access.syncUrl ?? access.serverUrl),
104
+ serverUrl: normalizeServerUrl(access.syncUrl ?? access.serverUrl),
105
+ };
106
+ return {
107
+ ...normalized,
108
+ kind,
109
+ roomUrl: roomUrlForAccess(normalized),
110
+ serverRoomUrl: serverRoomUrlForAccess(normalized),
111
+ };
112
+ }
113
+ function roomPathFromUrl(url) {
114
+ const parts = url.pathname.split('/').filter(Boolean);
115
+ const roomIndex = parts.lastIndexOf('room');
116
+ if (roomIndex === -1 || roomIndex === parts.length - 1)
117
+ return null;
118
+ const serverPath = parts.slice(0, roomIndex).join('/');
119
+ const serverUrl = serverPath ? `${url.origin}/${serverPath}` : url.origin;
120
+ return {
121
+ roomId: decodeURIComponent(parts[roomIndex + 1] ?? ''),
122
+ serverUrl,
123
+ };
124
+ }
125
+ function isEncodedRoomToken(value) {
126
+ if (!value || typeof value !== 'object')
127
+ return false;
128
+ const candidate = value;
129
+ return (candidate.v === 1 &&
130
+ typeof candidate.roomId === 'string' &&
131
+ candidate.roomId.length > 0 &&
132
+ typeof candidate.roomSecret === 'string' &&
133
+ candidate.roomSecret.length > 0 &&
134
+ ((typeof candidate.serverUrl === 'string' && candidate.serverUrl.length > 0) ||
135
+ (typeof candidate.appUrl === 'string' && candidate.appUrl.length > 0) ||
136
+ (typeof candidate.syncUrl === 'string' && candidate.syncUrl.length > 0)));
137
+ }
138
+ function randomBase64Url(byteLength) {
139
+ const bytes = webcrypto.getRandomValues(new Uint8Array(byteLength));
140
+ return Buffer.from(bytes).toString('base64url');
141
+ }
142
+ //# sourceMappingURL=room-reference.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"room-reference.js","sourceRoot":"","sources":["../../../../src/rooms/room-reference.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,26 @@
1
+ import type { EncryptedPayload } from './crypto.js';
2
+ import type { EncryptedUpdateRecord, IncomingEncryptedUpdate } from '../server/append-log.js';
3
+ import type { ProjectSnapshot } from './project-state.js';
4
+ import type { RoomAccess } from './room-reference.js';
5
+ export declare const TIMELINE_EVENT_SCHEMA = "fold.timeline-event.v1";
6
+ export declare const TIMELINE_EVENT_SENDER_ID_PREFIX = "fold-cli:event";
7
+ export type TimelineEventType = 'publish' | 'file_posted' | 'proposal_submitted' | 'proposal_accepted' | 'proposal_rejected' | 'export';
8
+ export interface TimelineEvent {
9
+ schema: typeof TIMELINE_EVENT_SCHEMA;
10
+ id: string;
11
+ type: TimelineEventType;
12
+ createdAt: string;
13
+ actorPersonaId: string;
14
+ proposalId: string | null;
15
+ documentSha256: string | null;
16
+ message: string;
17
+ acceptedProject?: ProjectSnapshot;
18
+ }
19
+ export declare function createEncryptedTimelineEvent(access: RoomAccess, event: TimelineEvent): Promise<IncomingEncryptedUpdate>;
20
+ export declare function createTimelineEvent(input: Omit<TimelineEvent, 'schema' | 'id' | 'createdAt'> & {
21
+ idSeed?: string;
22
+ createdAt?: string;
23
+ }): TimelineEvent;
24
+ export declare function decryptTimelineEventsFromRecords(access: RoomAccess, records: EncryptedUpdateRecord[]): Promise<TimelineEvent[]>;
25
+ export declare function replayTimelineEvents(events: TimelineEvent[]): TimelineEvent[];
26
+ export declare function decryptTimelineEvent(access: RoomAccess, payload: EncryptedPayload, senderId: string): Promise<TimelineEvent>;
@@ -0,0 +1,68 @@
1
+ import { randomUUID } from 'node:crypto';
2
+ import { decryptJsonRecord, encryptJsonRecord } from './encrypted-records.js';
3
+ export const TIMELINE_EVENT_SCHEMA = 'fold.timeline-event.v1';
4
+ export const TIMELINE_EVENT_SENDER_ID_PREFIX = 'fold-cli:event';
5
+ export async function createEncryptedTimelineEvent(access, event) {
6
+ const senderId = `${TIMELINE_EVENT_SENDER_ID_PREFIX}:${event.id}`;
7
+ return encryptJsonRecord(access, senderId, event);
8
+ }
9
+ export function createTimelineEvent(input) {
10
+ const createdAt = input.createdAt ?? new Date().toISOString();
11
+ return {
12
+ schema: TIMELINE_EVENT_SCHEMA,
13
+ id: randomUUID(),
14
+ type: input.type,
15
+ createdAt,
16
+ actorPersonaId: input.actorPersonaId,
17
+ proposalId: input.proposalId,
18
+ documentSha256: input.documentSha256,
19
+ message: input.message,
20
+ acceptedProject: input.acceptedProject,
21
+ };
22
+ }
23
+ export async function decryptTimelineEventsFromRecords(access, records) {
24
+ const events = [];
25
+ for (const record of records) {
26
+ if (!record.senderId.startsWith(TIMELINE_EVENT_SENDER_ID_PREFIX))
27
+ continue;
28
+ events.push(await decryptTimelineEvent(access, record, record.senderId));
29
+ }
30
+ return replayTimelineEvents(events);
31
+ }
32
+ export function replayTimelineEvents(events) {
33
+ return [...events].sort((left, right) => left.createdAt.localeCompare(right.createdAt));
34
+ }
35
+ export async function decryptTimelineEvent(access, payload, senderId) {
36
+ const value = await decryptJsonRecord(access, payload, senderId);
37
+ if (!isTimelineEvent(value)) {
38
+ throw new Error('Invalid encrypted timeline event payload');
39
+ }
40
+ return value;
41
+ }
42
+ function isTimelineEvent(value) {
43
+ if (!value || typeof value !== 'object')
44
+ return false;
45
+ const candidate = value;
46
+ return (candidate.schema === TIMELINE_EVENT_SCHEMA &&
47
+ isTimelineEventType(candidate.type) &&
48
+ typeof candidate.id === 'string' &&
49
+ typeof candidate.createdAt === 'string' &&
50
+ typeof candidate.actorPersonaId === 'string' &&
51
+ (typeof candidate.proposalId === 'string' || candidate.proposalId === null) &&
52
+ (typeof candidate.documentSha256 === 'string' || candidate.documentSha256 === null) &&
53
+ typeof candidate.message === 'string' &&
54
+ (candidate.acceptedProject === undefined ||
55
+ (candidate.acceptedProject.schema === 'fold.project.v1' &&
56
+ typeof candidate.acceptedProject.primaryPath === 'string' &&
57
+ typeof candidate.acceptedProject.updatedAt === 'string' &&
58
+ Array.isArray(candidate.acceptedProject.files))));
59
+ }
60
+ function isTimelineEventType(value) {
61
+ return (value === 'publish' ||
62
+ value === 'file_posted' ||
63
+ value === 'proposal_submitted' ||
64
+ value === 'proposal_accepted' ||
65
+ value === 'proposal_rejected' ||
66
+ value === 'export');
67
+ }
68
+ //# sourceMappingURL=timeline.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"timeline.js","sourceRoot":"","sources":["../../../../src/rooms/timeline.ts"],"names":[],"mappings":"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"}
package/package.json ADDED
@@ -0,0 +1,35 @@
1
+ {
2
+ "name": "fold-agent",
3
+ "version": "0.1.0",
4
+ "description": "Fold CLI for encrypted Markdown project rooms used by humans and agents.",
5
+ "type": "module",
6
+ "license": "MIT",
7
+ "engines": {
8
+ "node": ">=22"
9
+ },
10
+ "bin": {
11
+ "fold-agent": "./bin/fold-agent.js"
12
+ },
13
+ "files": [
14
+ "bin",
15
+ "dist/cli",
16
+ "dist/deploy",
17
+ "dist/rooms",
18
+ "skills/fold",
19
+ "README.md"
20
+ ],
21
+ "publishConfig": {
22
+ "access": "public"
23
+ },
24
+ "scripts": {
25
+ "build": "tsc -p tsconfig.json",
26
+ "prepack": "npm run build"
27
+ },
28
+ "dependencies": {
29
+ "@stricli/core": "^1.2.7",
30
+ "prosemirror-markdown": "^1.13.4",
31
+ "prosemirror-model": "^1.25.7",
32
+ "ws": "^8.18.0",
33
+ "yjs": "^13.6.23"
34
+ }
35
+ }