@claudexor/daemon 1.0.0 → 2.1.2

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 (105) hide show
  1. package/dist/client.d.ts +29 -1
  2. package/dist/client.d.ts.map +1 -1
  3. package/dist/client.js +24 -4
  4. package/dist/client.js.map +1 -1
  5. package/dist/command-authority.d.ts +11 -0
  6. package/dist/command-authority.d.ts.map +1 -0
  7. package/dist/command-authority.js +15 -0
  8. package/dist/command-authority.js.map +1 -0
  9. package/dist/command-retention.d.ts +5 -0
  10. package/dist/command-retention.d.ts.map +1 -0
  11. package/dist/command-retention.js +18 -0
  12. package/dist/command-retention.js.map +1 -0
  13. package/dist/command-store.d.ts +41 -0
  14. package/dist/command-store.d.ts.map +1 -0
  15. package/dist/command-store.js +197 -0
  16. package/dist/command-store.js.map +1 -0
  17. package/dist/events.d.ts +2 -3
  18. package/dist/events.d.ts.map +1 -1
  19. package/dist/events.js +2 -3
  20. package/dist/events.js.map +1 -1
  21. package/dist/index.d.ts +13 -0
  22. package/dist/index.d.ts.map +1 -1
  23. package/dist/index.js +13 -0
  24. package/dist/index.js.map +1 -1
  25. package/dist/interactions.d.ts +32 -22
  26. package/dist/interactions.d.ts.map +1 -1
  27. package/dist/interactions.js +183 -56
  28. package/dist/interactions.js.map +1 -1
  29. package/dist/journal-events.d.ts +4 -0
  30. package/dist/journal-events.d.ts.map +1 -0
  31. package/dist/journal-events.js +23 -0
  32. package/dist/journal-events.js.map +1 -0
  33. package/dist/journal-manager.d.ts +72 -0
  34. package/dist/journal-manager.d.ts.map +1 -0
  35. package/dist/journal-manager.js +463 -0
  36. package/dist/journal-manager.js.map +1 -0
  37. package/dist/journal-projection.d.ts +11 -0
  38. package/dist/journal-projection.d.ts.map +1 -0
  39. package/dist/journal-projection.js +2 -0
  40. package/dist/journal-projection.js.map +1 -0
  41. package/dist/journal-recovery-files.d.ts +27 -0
  42. package/dist/journal-recovery-files.d.ts.map +1 -0
  43. package/dist/journal-recovery-files.js +209 -0
  44. package/dist/journal-recovery-files.js.map +1 -0
  45. package/dist/operator-decisions.d.ts +30 -0
  46. package/dist/operator-decisions.d.ts.map +1 -0
  47. package/dist/operator-decisions.js +137 -0
  48. package/dist/operator-decisions.js.map +1 -0
  49. package/dist/project-partitions.d.ts +167 -0
  50. package/dist/project-partitions.d.ts.map +1 -0
  51. package/dist/project-partitions.js +365 -0
  52. package/dist/project-partitions.js.map +1 -0
  53. package/dist/projects.d.ts +29 -0
  54. package/dist/projects.d.ts.map +1 -0
  55. package/dist/projects.js +180 -0
  56. package/dist/projects.js.map +1 -0
  57. package/dist/quota-registry.d.ts +75 -0
  58. package/dist/quota-registry.d.ts.map +1 -0
  59. package/dist/quota-registry.js +208 -0
  60. package/dist/quota-registry.js.map +1 -0
  61. package/dist/resource-store.d.ts +22 -0
  62. package/dist/resource-store.d.ts.map +1 -0
  63. package/dist/resource-store.js +272 -0
  64. package/dist/resource-store.js.map +1 -0
  65. package/dist/run-events.d.ts +15 -0
  66. package/dist/run-events.d.ts.map +1 -0
  67. package/dist/run-events.js +29 -0
  68. package/dist/run-events.js.map +1 -0
  69. package/dist/server.d.ts +33 -33
  70. package/dist/server.d.ts.map +1 -1
  71. package/dist/server.js +231 -238
  72. package/dist/server.js.map +1 -1
  73. package/dist/spec-sessions.d.ts +72 -0
  74. package/dist/spec-sessions.d.ts.map +1 -0
  75. package/dist/spec-sessions.js +301 -0
  76. package/dist/spec-sessions.js.map +1 -0
  77. package/dist/terminate.d.ts +52 -0
  78. package/dist/terminate.d.ts.map +1 -0
  79. package/dist/terminate.js +111 -0
  80. package/dist/terminate.js.map +1 -0
  81. package/dist/thread-head-ping.d.ts +35 -0
  82. package/dist/thread-head-ping.d.ts.map +1 -0
  83. package/dist/thread-head-ping.js +67 -0
  84. package/dist/thread-head-ping.js.map +1 -0
  85. package/dist/thread-lifecycle.d.ts +5 -0
  86. package/dist/thread-lifecycle.d.ts.map +1 -0
  87. package/dist/thread-lifecycle.js +57 -0
  88. package/dist/thread-lifecycle.js.map +1 -0
  89. package/dist/thread-store-support.d.ts +37 -0
  90. package/dist/thread-store-support.d.ts.map +1 -0
  91. package/dist/thread-store-support.js +94 -0
  92. package/dist/thread-store-support.js.map +1 -0
  93. package/dist/threads.d.ts +59 -13
  94. package/dist/threads.d.ts.map +1 -1
  95. package/dist/threads.js +254 -141
  96. package/dist/threads.js.map +1 -1
  97. package/dist/token.d.ts +13 -4
  98. package/dist/token.d.ts.map +1 -1
  99. package/dist/token.js +134 -29
  100. package/dist/token.js.map +1 -1
  101. package/dist/writer-lease.d.ts +21 -0
  102. package/dist/writer-lease.d.ts.map +1 -0
  103. package/dist/writer-lease.js +94 -0
  104. package/dist/writer-lease.js.map +1 -0
  105. package/package.json +5 -4
@@ -0,0 +1,75 @@
1
+ import type { DurableJournal } from "@claudexor/journal";
2
+ import { type QuotaSnapshot } from "@claudexor/schema";
3
+ export type QuotaRefresher = () => Promise<QuotaSnapshot[]>;
4
+ /** Global-journal authority for vendor-owned quota snapshots. */
5
+ export declare class QuotaRegistry {
6
+ private readonly journal;
7
+ private readonly refreshers;
8
+ private readonly now;
9
+ private readonly snapshots;
10
+ private pollFailures;
11
+ private pollNotBefore;
12
+ constructor(journal: DurableJournal, refreshers?: readonly QuotaRefresher[], now?: () => Date);
13
+ read(): {
14
+ snapshots: {
15
+ source: "codex_app_server" | "codex_rollout" | "claude_statusline" | "claude_api_retry" | "claude_oauth_usage";
16
+ subject: {
17
+ harness: string;
18
+ credential_route: "local" | "vendor_native" | "managed_api_key";
19
+ plan_label: string | null;
20
+ subject_id: string | null;
21
+ };
22
+ constraints: {
23
+ id: string;
24
+ label: string;
25
+ used_ratio: number | null;
26
+ window_seconds: number | null;
27
+ resets_at: string | null;
28
+ cooldown_until: string | null;
29
+ }[];
30
+ observed_at: string;
31
+ freshness: "unknown" | "fresh" | "stale";
32
+ }[];
33
+ refreshed_at: string | null;
34
+ };
35
+ /** Freshness-annotated snapshots with expired (>24h) observations pruned.
36
+ * An old observation whose constraint still EXTENDS into the future (a
37
+ * weekly cooldown/reset seen once) is kept and stale-marked: pruning it
38
+ * would hide a live cap from both the footer and the router's ledger. */
39
+ private activeSnapshots;
40
+ refresh(): Promise<{
41
+ snapshots: {
42
+ source: "codex_app_server" | "codex_rollout" | "claude_statusline" | "claude_api_retry" | "claude_oauth_usage";
43
+ subject: {
44
+ harness: string;
45
+ credential_route: "local" | "vendor_native" | "managed_api_key";
46
+ plan_label: string | null;
47
+ subject_id: string | null;
48
+ };
49
+ constraints: {
50
+ id: string;
51
+ label: string;
52
+ used_ratio: number | null;
53
+ window_seconds: number | null;
54
+ resets_at: string | null;
55
+ cooldown_until: string | null;
56
+ }[];
57
+ observed_at: string;
58
+ freshness: "unknown" | "fresh" | "stale";
59
+ }[];
60
+ refreshed_at: string | null;
61
+ }>;
62
+ /** Background official-source refresh for empty/stale projections with bounded backoff. */
63
+ pollStale(): Promise<boolean>;
64
+ ingest(harnessId: string, value: unknown): void;
65
+ upsert(value: QuotaSnapshot): void;
66
+ validateProjection(): void;
67
+ private upsertCooldown;
68
+ private apply;
69
+ }
70
+ export declare function quotaProjection(refreshers?: readonly QuotaRefresher[]): {
71
+ name: string;
72
+ create: (journal: DurableJournal) => QuotaRegistry;
73
+ validate: (registry: QuotaRegistry) => void;
74
+ };
75
+ //# sourceMappingURL=quota-registry.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"quota-registry.d.ts","sourceRoot":"","sources":["../src/quota-registry.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,oBAAoB,CAAC;AACzD,OAAO,EAKL,KAAK,aAAa,EACnB,MAAM,mBAAmB,CAAC;AAU3B,MAAM,MAAM,cAAc,GAAG,MAAM,OAAO,CAAC,aAAa,EAAE,CAAC,CAAC;AAE5D,iEAAiE;AACjE,qBAAa,aAAa;IAMtB,OAAO,CAAC,QAAQ,CAAC,OAAO;IACxB,OAAO,CAAC,QAAQ,CAAC,UAAU;IAC3B,OAAO,CAAC,QAAQ,CAAC,GAAG;IAPtB,OAAO,CAAC,QAAQ,CAAC,SAAS,CAAoC;IAC9D,OAAO,CAAC,YAAY,CAAK;IACzB,OAAO,CAAC,aAAa,CAAK;gBAGP,OAAO,EAAE,cAAc,EACvB,UAAU,GAAE,SAAS,cAAc,EAAO,EAC1C,GAAG,GAAE,MAAM,IAAuB;IAQrD,IAAI;;;;;;;;;;;;;;;;;;;;;;IAOJ;;;6EAGyE;IACzE,OAAO,CAAC,eAAe;IAgBjB,OAAO;;;;;;;;;;;;;;;;;;;;;;IA6Bb,2FAA2F;IACrF,SAAS,IAAI,OAAO,CAAC,OAAO,CAAC;IAmBnC,MAAM,CAAC,SAAS,EAAE,MAAM,EAAE,KAAK,EAAE,OAAO,GAAG,IAAI;IA4B/C,MAAM,CAAC,KAAK,EAAE,aAAa,GAAG,IAAI;IAMlC,kBAAkB,IAAI,IAAI;IAI1B,OAAO,CAAC,cAAc;IAgDtB,OAAO,CAAC,KAAK;CAGd;AAED,wBAAgB,eAAe,CAAC,UAAU,GAAE,SAAS,cAAc,EAAO;;sBAGpD,cAAc;yBACX,aAAa;EAErC"}
@@ -0,0 +1,208 @@
1
+ import { ControlQuotaResponse, HarnessEvent, QuotaSnapshot as QuotaSnapshotSchema, } from "@claudexor/schema";
2
+ const UPSERTED = "quota.snapshot.upserted";
3
+ const POLL_BACKOFF_MS = 60_000;
4
+ const MAX_POLL_BACKOFF_MS = 15 * 60_000;
5
+ /** Snapshots older than this are pruned from every projection read (W17):
6
+ * a day-old observation is no longer quota truth — surfacing it as a "stale"
7
+ * row forever just clutters the footer with dead subjects. */
8
+ const MAX_SNAPSHOT_AGE_MS = 24 * 60 * 60_000;
9
+ /** Global-journal authority for vendor-owned quota snapshots. */
10
+ export class QuotaRegistry {
11
+ journal;
12
+ refreshers;
13
+ now;
14
+ snapshots = new Map();
15
+ pollFailures = 0;
16
+ pollNotBefore = 0;
17
+ constructor(journal, refreshers = [], now = () => new Date()) {
18
+ this.journal = journal;
19
+ this.refreshers = refreshers;
20
+ this.now = now;
21
+ for (const record of journal.records()) {
22
+ if (record.type === UPSERTED)
23
+ this.apply(QuotaSnapshotSchema.parse(record.payload));
24
+ }
25
+ this.validateProjection();
26
+ }
27
+ read() {
28
+ return ControlQuotaResponse.parse({
29
+ snapshots: this.activeSnapshots(this.now().getTime()),
30
+ refreshed_at: null,
31
+ });
32
+ }
33
+ /** Freshness-annotated snapshots with expired (>24h) observations pruned.
34
+ * An old observation whose constraint still EXTENDS into the future (a
35
+ * weekly cooldown/reset seen once) is kept and stale-marked: pruning it
36
+ * would hide a live cap from both the footer and the router's ledger. */
37
+ activeSnapshots(now) {
38
+ return [...this.snapshots.values()]
39
+ .filter((snapshot) => {
40
+ const observed = Date.parse(snapshot.observed_at);
41
+ if (!Number.isFinite(observed))
42
+ return false;
43
+ if (now - observed <= MAX_SNAPSHOT_AGE_MS)
44
+ return true;
45
+ return snapshot.constraints.some((constraint) => [constraint.cooldown_until, constraint.resets_at].some((raw) => {
46
+ const at = raw ? Date.parse(raw) : Number.NaN;
47
+ return Number.isFinite(at) && at > now;
48
+ }));
49
+ })
50
+ .map((snapshot) => staleAt(snapshot, now));
51
+ }
52
+ async refresh() {
53
+ if (this.refreshers.length === 0) {
54
+ throw Object.assign(new Error("no live vendor-owned quota refresh source is available"), {
55
+ code: "quota_refresh_unavailable",
56
+ status: 503,
57
+ });
58
+ }
59
+ let successfulSources = 0;
60
+ const failures = [];
61
+ for (const refresher of this.refreshers) {
62
+ try {
63
+ for (const snapshot of await refresher())
64
+ this.upsert(snapshot);
65
+ successfulSources += 1;
66
+ }
67
+ catch (error) {
68
+ failures.push(error instanceof Error ? error.message : String(error));
69
+ }
70
+ }
71
+ if (successfulSources === 0) {
72
+ throw Object.assign(new Error(`quota refresh failed: ${failures.join("; ")}`), {
73
+ code: "quota_refresh_unavailable",
74
+ status: 503,
75
+ });
76
+ }
77
+ return ControlQuotaResponse.parse({
78
+ snapshots: this.activeSnapshots(this.now().getTime()),
79
+ refreshed_at: this.now().toISOString(),
80
+ });
81
+ }
82
+ /** Background official-source refresh for empty/stale projections with bounded backoff. */
83
+ async pollStale() {
84
+ const now = this.now().getTime();
85
+ const snapshots = this.activeSnapshots(now);
86
+ if (snapshots.length > 0 && snapshots.every((snapshot) => snapshot.freshness === "fresh"))
87
+ return false;
88
+ if (now < this.pollNotBefore)
89
+ return false;
90
+ try {
91
+ await this.refresh();
92
+ this.pollFailures = 0;
93
+ this.pollNotBefore = now + POLL_BACKOFF_MS;
94
+ return true;
95
+ }
96
+ catch {
97
+ this.pollFailures += 1;
98
+ this.pollNotBefore =
99
+ now + Math.min(POLL_BACKOFF_MS * 2 ** (this.pollFailures - 1), MAX_POLL_BACKOFF_MS);
100
+ return false;
101
+ }
102
+ }
103
+ ingest(harnessId, value) {
104
+ const event = HarnessEvent.safeParse(value);
105
+ if (!event.success)
106
+ return;
107
+ const quota = event.data.quota;
108
+ const credentialRoute = event.data.credential_route;
109
+ if (quota && credentialRoute) {
110
+ this.upsert({
111
+ subject: {
112
+ harness: harnessId,
113
+ credential_route: credentialRoute,
114
+ plan_label: quota.plan_label,
115
+ // Reconcile the subject with the event's Claudexor profile stamp
116
+ // (round-17 #2): a profiled run's quota must never register as the
117
+ // engine-default subject just because the vendor record carries no
118
+ // subject of its own. The profile stamp is the credential identity.
119
+ subject_id: event.data.credential_profile_id ?? quota.subject_id ?? null,
120
+ },
121
+ constraints: quota.constraints,
122
+ source: quota.source,
123
+ observed_at: event.data.ts,
124
+ freshness: "fresh",
125
+ });
126
+ }
127
+ if (event.data.rate_limit && credentialRoute && ["codex", "claude"].includes(harnessId)) {
128
+ this.upsertCooldown(harnessId, credentialRoute, event.data);
129
+ }
130
+ }
131
+ upsert(value) {
132
+ const snapshot = QuotaSnapshotSchema.parse(value);
133
+ this.journal.append(UPSERTED, snapshot);
134
+ this.apply(snapshot);
135
+ }
136
+ validateProjection() {
137
+ for (const snapshot of this.snapshots.values())
138
+ QuotaSnapshotSchema.parse(snapshot);
139
+ }
140
+ upsertCooldown(harness, credentialRoute, event) {
141
+ const reset = event.rate_limit?.resets_at ?? null;
142
+ const delay = event.rate_limit?.retry_delay_ms ?? null;
143
+ const cooldownUntil = reset ??
144
+ new Date(this.now().getTime() + (typeof delay === "number" ? delay : 5 * 60_000)).toISOString();
145
+ const source = harness === "claude" ? "claude_api_retry" : "codex_rollout";
146
+ // The event's profile stamp scopes the cooldown to ITS subject (release
147
+ // wave round-11): a profiled limit must never cool the default subject
148
+ // down (or vice versa), and two profiles never share one quota key.
149
+ const profileId = event.credential_profile_id ?? null;
150
+ const existing = [...this.snapshots.values()].find((snapshot) => snapshot.subject.harness === harness &&
151
+ snapshot.subject.credential_route === credentialRoute &&
152
+ (snapshot.subject.subject_id ?? null) === profileId &&
153
+ snapshot.source === source);
154
+ this.upsert({
155
+ subject: existing?.subject ?? {
156
+ harness,
157
+ credential_route: credentialRoute,
158
+ plan_label: null,
159
+ subject_id: profileId,
160
+ },
161
+ source,
162
+ observed_at: event.ts,
163
+ freshness: "fresh",
164
+ constraints: [
165
+ ...(existing?.constraints.filter((constraint) => constraint.id !== "cooldown") ?? []),
166
+ {
167
+ id: "cooldown",
168
+ label: "Cooldown",
169
+ used_ratio: null,
170
+ window_seconds: null,
171
+ resets_at: reset,
172
+ cooldown_until: cooldownUntil,
173
+ },
174
+ ],
175
+ });
176
+ }
177
+ apply(snapshot) {
178
+ this.snapshots.set(snapshotKey(snapshot), snapshot);
179
+ }
180
+ }
181
+ export function quotaProjection(refreshers = []) {
182
+ return {
183
+ name: "quota",
184
+ create: (journal) => new QuotaRegistry(journal, refreshers),
185
+ validate: (registry) => registry.validateProjection(),
186
+ };
187
+ }
188
+ function snapshotKey(snapshot) {
189
+ const subject = snapshot.subject;
190
+ return [
191
+ subject.harness,
192
+ subject.credential_route,
193
+ subject.subject_id ?? "",
194
+ snapshot.source,
195
+ ].join("\0");
196
+ }
197
+ function staleAt(snapshot, now) {
198
+ if (snapshot.freshness !== "fresh")
199
+ return snapshot;
200
+ const observed = Date.parse(snapshot.observed_at);
201
+ const resetExpired = snapshot.constraints.some((constraint) => {
202
+ const reset = constraint.resets_at ? Date.parse(constraint.resets_at) : Number.NaN;
203
+ return Number.isFinite(reset) && reset <= now;
204
+ });
205
+ const tooOld = !Number.isFinite(observed) || now - observed > 5 * 60_000;
206
+ return resetExpired || tooOld ? { ...snapshot, freshness: "stale" } : snapshot;
207
+ }
208
+ //# sourceMappingURL=quota-registry.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"quota-registry.js","sourceRoot":"","sources":["../src/quota-registry.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,22 @@
1
+ import { Attachment, ControlResource, ControlUploadStatus, type ResourceAttachmentRef } from "@claudexor/schema";
2
+ /** Daemon-owned immutable content store. Uploads are single-shot streams; finalized blobs dedupe. */
3
+ export declare class ResourceStore {
4
+ private readonly uploads;
5
+ private readonly createIdempotency;
6
+ private readonly finalizeIdempotency;
7
+ private readonly uploadsDir;
8
+ private readonly blobsDir;
9
+ private readonly resourcesDir;
10
+ private readonly idempotencyDir;
11
+ constructor(root: string);
12
+ create(raw: unknown, idempotencyKey: string): ReturnType<typeof ControlUploadStatus.parse>;
13
+ status(uploadId: string): ReturnType<typeof ControlUploadStatus.parse>;
14
+ write(uploadId: string, chunks: AsyncIterable<Uint8Array>): Promise<ReturnType<typeof ControlUploadStatus.parse>>;
15
+ cancel(uploadId: string): ReturnType<typeof ControlUploadStatus.parse>;
16
+ finalize(uploadId: string, expectedSha256: string | undefined, idempotencyKey: string): ReturnType<typeof ControlResource.parse>;
17
+ resolve(refs: ResourceAttachmentRef[] | undefined): Attachment[];
18
+ private persistUpload;
19
+ private persistIdempotency;
20
+ private restoreState;
21
+ }
22
+ //# sourceMappingURL=resource-store.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"resource-store.d.ts","sourceRoot":"","sources":["../src/resource-store.ts"],"names":[],"mappings":"AAgBA,OAAO,EACL,UAAU,EACV,eAAe,EAEf,mBAAmB,EACnB,KAAK,qBAAqB,EAC3B,MAAM,mBAAmB,CAAC;AAgC3B,qGAAqG;AACrG,qBAAa,aAAa;IACxB,OAAO,CAAC,QAAQ,CAAC,OAAO,CAAmC;IAC3D,OAAO,CAAC,QAAQ,CAAC,iBAAiB,CAG9B;IACJ,OAAO,CAAC,QAAQ,CAAC,mBAAmB,CAGhC;IACJ,OAAO,CAAC,QAAQ,CAAC,UAAU,CAAS;IACpC,OAAO,CAAC,QAAQ,CAAC,QAAQ,CAAS;IAClC,OAAO,CAAC,QAAQ,CAAC,YAAY,CAAS;IACtC,OAAO,CAAC,QAAQ,CAAC,cAAc,CAAS;gBAE5B,IAAI,EAAE,MAAM;IAUxB,MAAM,CAAC,GAAG,EAAE,OAAO,EAAE,cAAc,EAAE,MAAM,GAAG,UAAU,CAAC,OAAO,mBAAmB,CAAC,KAAK,CAAC;IAiC1F,MAAM,CAAC,QAAQ,EAAE,MAAM,GAAG,UAAU,CAAC,OAAO,mBAAmB,CAAC,KAAK,CAAC;IAMhE,KAAK,CACT,QAAQ,EAAE,MAAM,EAChB,MAAM,EAAE,aAAa,CAAC,UAAU,CAAC,GAChC,OAAO,CAAC,UAAU,CAAC,OAAO,mBAAmB,CAAC,KAAK,CAAC,CAAC;IA2CxD,MAAM,CAAC,QAAQ,EAAE,MAAM,GAAG,UAAU,CAAC,OAAO,mBAAmB,CAAC,KAAK,CAAC;IAStE,QAAQ,CACN,QAAQ,EAAE,MAAM,EAChB,cAAc,EAAE,MAAM,GAAG,SAAS,EAClC,cAAc,EAAE,MAAM,GACrB,UAAU,CAAC,OAAO,eAAe,CAAC,KAAK,CAAC;IAyD3C,OAAO,CAAC,IAAI,EAAE,qBAAqB,EAAE,GAAG,SAAS,GAAG,UAAU,EAAE;IAuChE,OAAO,CAAC,aAAa;IAOrB,OAAO,CAAC,kBAAkB;IAK1B,OAAO,CAAC,YAAY;CAgDrB"}
@@ -0,0 +1,272 @@
1
+ import { createHash } from "node:crypto";
2
+ import { closeSync, existsSync, fsyncSync, ftruncateSync, mkdirSync, openSync, readFileSync, readdirSync, renameSync, rmSync, writeFileSync, writeSync, } from "node:fs";
3
+ import { join } from "node:path";
4
+ import { Attachment, ControlResource, ControlUploadCreateRequest, ControlUploadStatus, } from "@claudexor/schema";
5
+ import { hashJson, newId, sensitiveResourcePolicy } from "@claudexor/util";
6
+ function resourceError(message, status = 400, code = "resource_error") {
7
+ return Object.assign(new Error(message), { status, code });
8
+ }
9
+ function atomicJson(path, value) {
10
+ const temp = `${path}.${newId("tmp")}`;
11
+ writeFileSync(temp, `${JSON.stringify(value)}\n`, { mode: 0o600 });
12
+ const fd = openSync(temp, "r");
13
+ try {
14
+ fsyncSync(fd);
15
+ }
16
+ finally {
17
+ closeSync(fd);
18
+ }
19
+ renameSync(temp, path);
20
+ }
21
+ /** Daemon-owned immutable content store. Uploads are single-shot streams; finalized blobs dedupe. */
22
+ export class ResourceStore {
23
+ uploads = new Map();
24
+ createIdempotency = new Map();
25
+ finalizeIdempotency = new Map();
26
+ uploadsDir;
27
+ blobsDir;
28
+ resourcesDir;
29
+ idempotencyDir;
30
+ constructor(root) {
31
+ this.uploadsDir = join(root, "uploads");
32
+ this.blobsDir = join(root, "blobs");
33
+ this.resourcesDir = join(root, "resources");
34
+ this.idempotencyDir = join(root, "idempotency");
35
+ for (const dir of [this.uploadsDir, this.blobsDir, this.resourcesDir, this.idempotencyDir])
36
+ mkdirSync(dir, { recursive: true, mode: 0o700 });
37
+ this.restoreState();
38
+ }
39
+ create(raw, idempotencyKey) {
40
+ const request = ControlUploadCreateRequest.parse(raw);
41
+ if (sensitiveResourcePolicy.classifyPath(request.name).sensitive) {
42
+ throw sensitiveResourceError();
43
+ }
44
+ const requestDigest = hashJson(request);
45
+ const prior = this.createIdempotency.get(idempotencyKey);
46
+ if (prior) {
47
+ if (prior.requestDigest !== requestDigest)
48
+ throw idempotencyConflict();
49
+ return ControlUploadStatus.parse(prior.result);
50
+ }
51
+ const uploadId = newId("upl");
52
+ const partPath = join(this.uploadsDir, `${uploadId}.part`);
53
+ const fd = openSync(partPath, "wx", 0o600);
54
+ closeSync(fd);
55
+ const status = ControlUploadStatus.parse({
56
+ uploadId,
57
+ state: "open",
58
+ receivedBytes: 0,
59
+ expectedBytes: request.sizeBytes,
60
+ });
61
+ this.uploads.set(uploadId, { request, status, partPath });
62
+ this.createIdempotency.set(idempotencyKey, { requestDigest, result: status });
63
+ this.persistUpload(this.uploads.get(uploadId));
64
+ this.persistIdempotency({
65
+ operation: "create",
66
+ key: idempotencyKey,
67
+ requestDigest,
68
+ result: status,
69
+ });
70
+ return status;
71
+ }
72
+ status(uploadId) {
73
+ const upload = this.uploads.get(uploadId);
74
+ if (!upload)
75
+ throw resourceError(`no such upload: ${uploadId}`, 404, "upload_not_found");
76
+ return ControlUploadStatus.parse(upload.status);
77
+ }
78
+ async write(uploadId, chunks) {
79
+ const upload = this.uploads.get(uploadId);
80
+ if (!upload)
81
+ throw resourceError(`no such upload: ${uploadId}`, 404, "upload_not_found");
82
+ if (upload.status.state !== "open")
83
+ throw resourceError(`upload ${uploadId} is ${upload.status.state}`, 409, "upload_not_open");
84
+ upload.status = { ...upload.status, state: "uploading" };
85
+ const fd = openSync(upload.partPath, "r+");
86
+ try {
87
+ ftruncateSync(fd, 0);
88
+ upload.status = { ...upload.status, receivedBytes: 0 };
89
+ this.persistUpload(upload);
90
+ for await (const chunk of chunks) {
91
+ if (upload.status.state === "cancelled")
92
+ throw resourceError(`upload ${uploadId} was cancelled`, 409, "upload_cancelled");
93
+ const bytes = Buffer.from(chunk);
94
+ const next = upload.status.receivedBytes + bytes.length;
95
+ if (next > upload.status.expectedBytes)
96
+ throw resourceError("upload exceeds declared size", 413, "upload_size_exceeded");
97
+ writeSync(fd, bytes);
98
+ upload.status = { ...upload.status, receivedBytes: next };
99
+ }
100
+ if (upload.status.state === "cancelled")
101
+ throw resourceError(`upload ${uploadId} was cancelled`, 409, "upload_cancelled");
102
+ if (upload.status.receivedBytes !== upload.status.expectedBytes)
103
+ throw resourceError(`upload size mismatch: expected ${upload.status.expectedBytes}, received ${upload.status.receivedBytes}`, 400, "upload_size_mismatch");
104
+ fsyncSync(fd);
105
+ upload.status = { ...upload.status, state: "uploaded" };
106
+ this.persistUpload(upload);
107
+ return ControlUploadStatus.parse(upload.status);
108
+ }
109
+ catch (error) {
110
+ upload.status = { ...upload.status, state: "cancelled" };
111
+ rmSync(upload.partPath, { force: true });
112
+ this.persistUpload(upload);
113
+ throw error;
114
+ }
115
+ finally {
116
+ closeSync(fd);
117
+ }
118
+ }
119
+ cancel(uploadId) {
120
+ const upload = this.uploads.get(uploadId);
121
+ if (!upload)
122
+ throw resourceError(`no such upload: ${uploadId}`, 404, "upload_not_found");
123
+ upload.status = { ...upload.status, state: "cancelled" };
124
+ rmSync(upload.partPath, { force: true });
125
+ this.persistUpload(upload);
126
+ return ControlUploadStatus.parse(upload.status);
127
+ }
128
+ finalize(uploadId, expectedSha256, idempotencyKey) {
129
+ const requestDigest = hashJson({ uploadId, expectedSha256: expectedSha256 ?? null });
130
+ const prior = this.finalizeIdempotency.get(idempotencyKey);
131
+ if (prior) {
132
+ if (prior.requestDigest !== requestDigest)
133
+ throw idempotencyConflict();
134
+ return ControlResource.parse(prior.result);
135
+ }
136
+ const upload = this.uploads.get(uploadId);
137
+ if (!upload)
138
+ throw resourceError(`no such upload: ${uploadId}`, 404, "upload_not_found");
139
+ if (upload.status.state !== "uploaded")
140
+ throw resourceError(`upload ${uploadId} is ${upload.status.state}`, 409, "upload_not_uploaded");
141
+ const bytes = readFileSync(upload.partPath);
142
+ if (sensitiveResourcePolicy.containsSensitiveContent(bytes.toString("utf8"))) {
143
+ upload.status = { ...upload.status, state: "cancelled" };
144
+ rmSync(upload.partPath, { force: true });
145
+ this.persistUpload(upload);
146
+ throw sensitiveResourceError();
147
+ }
148
+ const sha256 = `sha256:${createHash("sha256").update(bytes).digest("hex")}`;
149
+ if (expectedSha256 !== undefined && expectedSha256 !== sha256)
150
+ throw resourceError("uploaded byte digest does not match expectedSha256", 409, "digest_mismatch");
151
+ const blobPath = join(this.blobsDir, sha256.slice("sha256:".length));
152
+ const deduplicated = existsSync(blobPath);
153
+ if (deduplicated)
154
+ rmSync(upload.partPath, { force: true });
155
+ else
156
+ renameSync(upload.partPath, blobPath);
157
+ const resourceId = newId("res");
158
+ const resource = ControlResource.parse({
159
+ resourceId,
160
+ kind: upload.request.kind,
161
+ mime: upload.request.mime,
162
+ name: upload.request.name,
163
+ sha256,
164
+ sizeBytes: bytes.length,
165
+ createdAt: new Date().toISOString(),
166
+ deduplicated,
167
+ });
168
+ atomicJson(join(this.resourcesDir, `${resourceId}.json`), resource);
169
+ this.uploads.delete(uploadId);
170
+ rmSync(join(this.uploadsDir, `${uploadId}.json`), { force: true });
171
+ this.finalizeIdempotency.set(idempotencyKey, { requestDigest, result: resource });
172
+ this.persistIdempotency({
173
+ operation: "finalize",
174
+ key: idempotencyKey,
175
+ requestDigest,
176
+ result: resource,
177
+ });
178
+ return resource;
179
+ }
180
+ resolve(refs) {
181
+ return (refs ?? []).map(({ resourceId }) => {
182
+ const metaPath = join(this.resourcesDir, `${resourceId}.json`);
183
+ if (!existsSync(metaPath))
184
+ throw resourceError(`no such resource: ${resourceId}`, 404, "resource_not_found");
185
+ const resource = ControlResource.parse(JSON.parse(readFileSync(metaPath, "utf8")));
186
+ const path = join(this.blobsDir, resource.sha256.slice("sha256:".length));
187
+ if (!existsSync(path))
188
+ throw resourceError(`resource blob is unavailable: ${resourceId}`, 409, "resource_unavailable");
189
+ const bytes = readFileSync(path);
190
+ if (sensitiveResourcePolicy.classifyPath(resource.name).sensitive ||
191
+ sensitiveResourcePolicy.containsSensitiveContent(bytes.toString("utf8"))) {
192
+ throw sensitiveResourceError();
193
+ }
194
+ const digest = `sha256:${createHash("sha256").update(bytes).digest("hex")}`;
195
+ if (bytes.length !== resource.sizeBytes || digest !== resource.sha256)
196
+ throw resourceError(`resource blob no longer matches finalized bytes: ${resourceId}`, 409, "resource_digest_mismatch");
197
+ return Attachment.parse({
198
+ resource_id: resource.resourceId,
199
+ kind: resource.kind,
200
+ mime: resource.mime,
201
+ name: resource.name,
202
+ sha256: resource.sha256,
203
+ size_bytes: resource.sizeBytes,
204
+ path,
205
+ });
206
+ });
207
+ }
208
+ persistUpload(upload) {
209
+ atomicJson(join(this.uploadsDir, `${upload.status.uploadId}.json`), {
210
+ request: upload.request,
211
+ status: upload.status,
212
+ });
213
+ }
214
+ persistIdempotency(record) {
215
+ const name = createHash("sha256").update(`${record.operation}\0${record.key}`).digest("hex");
216
+ atomicJson(join(this.idempotencyDir, `${name}.json`), record);
217
+ }
218
+ restoreState() {
219
+ for (const name of readdirSync(this.uploadsDir)) {
220
+ if (!name.endsWith(".json"))
221
+ continue;
222
+ try {
223
+ const raw = JSON.parse(readFileSync(join(this.uploadsDir, name), "utf8"));
224
+ const request = ControlUploadCreateRequest.parse(raw.request);
225
+ let status = ControlUploadStatus.parse(raw.status);
226
+ const partPath = join(this.uploadsDir, `${status.uploadId}.part`);
227
+ if (status.state === "uploading") {
228
+ status = { ...status, state: "open", receivedBytes: 0 };
229
+ }
230
+ if (status.state !== "cancelled" && !existsSync(partPath))
231
+ continue;
232
+ const upload = { request, status, partPath };
233
+ this.uploads.set(status.uploadId, upload);
234
+ this.persistUpload(upload);
235
+ }
236
+ catch {
237
+ // Invalid daemon-owned metadata is ignored; immutable resources remain independently verifiable.
238
+ }
239
+ }
240
+ for (const name of readdirSync(this.idempotencyDir)) {
241
+ if (!name.endsWith(".json"))
242
+ continue;
243
+ try {
244
+ const raw = JSON.parse(readFileSync(join(this.idempotencyDir, name), "utf8"));
245
+ if (typeof raw.key !== "string" || typeof raw.requestDigest !== "string")
246
+ continue;
247
+ if (raw.operation === "create") {
248
+ this.createIdempotency.set(raw.key, {
249
+ requestDigest: raw.requestDigest,
250
+ result: ControlUploadStatus.parse(raw.result),
251
+ });
252
+ }
253
+ else if (raw.operation === "finalize") {
254
+ this.finalizeIdempotency.set(raw.key, {
255
+ requestDigest: raw.requestDigest,
256
+ result: ControlResource.parse(raw.result),
257
+ });
258
+ }
259
+ }
260
+ catch {
261
+ // Fail closed on the individual binding: it cannot be used to claim a replay match.
262
+ }
263
+ }
264
+ }
265
+ }
266
+ function idempotencyConflict() {
267
+ return resourceError("idempotency key was already used with a different request", 409, "idempotency_conflict");
268
+ }
269
+ function sensitiveResourceError() {
270
+ return resourceError("resource rejected by sensitive-resource policy", 422, "sensitive_resource_rejected");
271
+ }
272
+ //# sourceMappingURL=resource-store.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"resource-store.js","sourceRoot":"","sources":["../src/resource-store.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,15 @@
1
+ import type { DurableJournal } from "@claudexor/journal";
2
+ import { type RunEvent as RunEventValue } from "@claudexor/schema";
3
+ /** Writes typed run events into their owning global/project journal partition. */
4
+ export declare class RunEventStore {
5
+ private readonly journal;
6
+ constructor(journal: DurableJournal);
7
+ record(value: RunEventValue): RunEventValue;
8
+ validateProjection(): void;
9
+ }
10
+ export declare function runEventProjection(): {
11
+ name: string;
12
+ create: (journal: DurableJournal) => RunEventStore;
13
+ validate: (store: RunEventStore) => void;
14
+ };
15
+ //# sourceMappingURL=run-events.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"run-events.d.ts","sourceRoot":"","sources":["../src/run-events.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,oBAAoB,CAAC;AACzD,OAAO,EAAY,KAAK,QAAQ,IAAI,aAAa,EAAE,MAAM,mBAAmB,CAAC;AAI7E,kFAAkF;AAClF,qBAAa,aAAa;IACZ,OAAO,CAAC,QAAQ,CAAC,OAAO;gBAAP,OAAO,EAAE,cAAc;IAIpD,MAAM,CAAC,KAAK,EAAE,aAAa,GAAG,aAAa;IAM3C,kBAAkB,IAAI,IAAI;CAK3B;AAED,wBAAgB,kBAAkB;;sBAGZ,cAAc;sBACd,aAAa;EAElC"}