@undefineds.co/linx 0.3.5 → 0.3.8
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.
- package/README.md +58 -23
- package/dist/generated/version.js +1 -1
- package/dist/generated/version.js.map +1 -1
- package/dist/index.js +336 -162
- package/dist/index.js.map +1 -1
- package/dist/lib/account-session.js +4 -8
- package/dist/lib/account-session.js.map +1 -1
- package/dist/lib/ai-command.js +228 -178
- package/dist/lib/ai-command.js.map +1 -1
- package/dist/lib/auto-mode/archive.js +38 -7
- package/dist/lib/auto-mode/archive.js.map +1 -1
- package/dist/lib/auto-mode/auth.js.map +1 -1
- package/dist/lib/auto-mode/display.js +71 -45
- package/dist/lib/auto-mode/display.js.map +1 -1
- package/dist/lib/auto-mode/format.js +9 -7
- package/dist/lib/auto-mode/format.js.map +1 -1
- package/dist/lib/auto-mode/hooks/claude.js +12 -2
- package/dist/lib/auto-mode/hooks/claude.js.map +1 -1
- package/dist/lib/auto-mode/hooks/codex.js +17 -7
- package/dist/lib/auto-mode/hooks/codex.js.map +1 -1
- package/dist/lib/auto-mode/hooks/index.js +28 -8
- package/dist/lib/auto-mode/hooks/index.js.map +1 -1
- package/dist/lib/auto-mode/pod-ai.js +20 -37
- package/dist/lib/auto-mode/pod-ai.js.map +1 -1
- package/dist/lib/auto-mode/pod-approval.js +124 -195
- package/dist/lib/auto-mode/pod-approval.js.map +1 -1
- package/dist/lib/auto-mode/pod-persistence.js +169 -90
- package/dist/lib/auto-mode/pod-persistence.js.map +1 -1
- package/dist/lib/auto-mode/runner.js +683 -81
- package/dist/lib/auto-mode/runner.js.map +1 -1
- package/dist/lib/auto-mode/secretary.js +186 -41
- package/dist/lib/auto-mode/secretary.js.map +1 -1
- package/dist/lib/auto-mode-command.js +32 -32
- package/dist/lib/auto-mode-command.js.map +1 -1
- package/dist/lib/chat-api.js +242 -50
- package/dist/lib/chat-api.js.map +1 -1
- package/dist/lib/codex-plugin/bridge.js +164 -17
- package/dist/lib/codex-plugin/bridge.js.map +1 -1
- package/dist/lib/codex-plugin/codex-native-proxy.js +370 -34
- package/dist/lib/codex-plugin/codex-native-proxy.js.map +1 -1
- package/dist/lib/credentials-store.js +33 -42
- package/dist/lib/credentials-store.js.map +1 -1
- package/dist/lib/linx-cloud-errors.js +61 -0
- package/dist/lib/linx-cloud-errors.js.map +1 -0
- package/dist/lib/linx-tui-contract.js +8 -5
- package/dist/lib/linx-tui-contract.js.map +1 -1
- package/dist/lib/login-command.js +9 -2
- package/dist/lib/login-command.js.map +1 -1
- package/dist/lib/models.js +3 -20
- package/dist/lib/models.js.map +1 -1
- package/dist/lib/oidc-auth.js +143 -17
- package/dist/lib/oidc-auth.js.map +1 -1
- package/dist/lib/oidc-session-storage.js +2 -6
- package/dist/lib/oidc-session-storage.js.map +1 -1
- package/dist/lib/pi-adapter/auto-input-controller.js +988 -0
- package/dist/lib/pi-adapter/auto-input-controller.js.map +1 -0
- package/dist/lib/pi-adapter/backend-command.js +2 -0
- package/dist/lib/pi-adapter/backend-command.js.map +1 -0
- package/dist/lib/pi-adapter/backend-credentials.js +80 -0
- package/dist/lib/pi-adapter/backend-credentials.js.map +1 -0
- package/dist/lib/pi-adapter/branding.js +246 -108
- package/dist/lib/pi-adapter/branding.js.map +1 -1
- package/dist/lib/pi-adapter/control-state.js +72 -0
- package/dist/lib/pi-adapter/control-state.js.map +1 -0
- package/dist/lib/pi-adapter/interactive.js +2634 -30
- package/dist/lib/pi-adapter/interactive.js.map +1 -1
- package/dist/lib/pi-adapter/pod-approval.js +382 -210
- package/dist/lib/pi-adapter/pod-approval.js.map +1 -1
- package/dist/lib/pi-adapter/pod-mirror-mapping.js +71 -17
- package/dist/lib/pi-adapter/pod-mirror-mapping.js.map +1 -1
- package/dist/lib/pi-adapter/pod-mirror.js +531 -64
- package/dist/lib/pi-adapter/pod-mirror.js.map +1 -1
- package/dist/lib/pi-adapter/pod-native.js +81 -85
- package/dist/lib/pi-adapter/pod-native.js.map +1 -1
- package/dist/lib/pi-adapter/pod-status-output.js +54 -0
- package/dist/lib/pi-adapter/pod-status-output.js.map +1 -0
- package/dist/lib/pi-adapter/runtime.js +458 -228
- package/dist/lib/pi-adapter/runtime.js.map +1 -1
- package/dist/lib/pi-adapter/session-control.js +509 -0
- package/dist/lib/pi-adapter/session-control.js.map +1 -0
- package/dist/lib/pi-adapter/session.js +35 -22
- package/dist/lib/pi-adapter/session.js.map +1 -1
- package/dist/lib/pi-adapter/stream.js +89 -32
- package/dist/lib/pi-adapter/stream.js.map +1 -1
- package/dist/lib/pi-adapter/sync-recovery.js +89 -0
- package/dist/lib/pi-adapter/sync-recovery.js.map +1 -0
- package/dist/lib/pi-adapter/web-fetch.js +13 -14
- package/dist/lib/pi-adapter/web-fetch.js.map +1 -1
- package/dist/lib/pod-chat-store.js +254 -78
- package/dist/lib/pod-chat-store.js.map +1 -1
- package/dist/lib/pod-data-session.js +156 -35
- package/dist/lib/pod-data-session.js.map +1 -1
- package/dist/lib/solid-auth-store.js +27 -0
- package/dist/lib/solid-auth-store.js.map +1 -0
- package/dist/lib/solid-auth.js +2 -4
- package/dist/lib/solid-auth.js.map +1 -1
- package/dist/lib/solid-client-credentials-login.js +100 -0
- package/dist/lib/solid-client-credentials-login.js.map +1 -0
- package/dist/lib/solid-local-store.js +31 -0
- package/dist/lib/solid-local-store.js.map +1 -0
- package/dist/lib/symphony/archive.js +328 -18
- package/dist/lib/symphony/archive.js.map +1 -1
- package/dist/lib/symphony/pod-projection.js +2222 -0
- package/dist/lib/symphony/pod-projection.js.map +1 -0
- package/dist/lib/symphony-command.js +602 -178
- package/dist/lib/symphony-command.js.map +1 -1
- package/dist/lib/sync-checkpoint-store.js +74 -0
- package/dist/lib/sync-checkpoint-store.js.map +1 -0
- package/dist/skills/symphony/SKILL.md +665 -0
- package/package.json +15 -9
- package/vendor/agent-runtime/dist/agent-runtime.d.ts +137 -0
- package/vendor/agent-runtime/dist/agent-runtime.js +211 -0
- package/vendor/agent-runtime/dist/auto-mode.d.ts +78 -13
- package/vendor/agent-runtime/dist/auto-mode.js +288 -31
- package/vendor/agent-runtime/dist/control-plane.d.ts +28 -0
- package/vendor/agent-runtime/dist/control-plane.js +79 -0
- package/vendor/agent-runtime/dist/file-sync.d.ts +157 -0
- package/vendor/agent-runtime/dist/file-sync.js +314 -0
- package/vendor/agent-runtime/dist/index.d.ts +7 -0
- package/vendor/agent-runtime/dist/index.js +7 -0
- package/vendor/agent-runtime/dist/reconciler.d.ts +117 -0
- package/vendor/agent-runtime/dist/reconciler.js +361 -0
- package/vendor/agent-runtime/dist/symphony.d.ts +128 -8
- package/vendor/agent-runtime/dist/symphony.js +362 -57
- package/vendor/agent-runtime/dist/sync.d.ts +271 -0
- package/vendor/agent-runtime/dist/sync.js +550 -0
- package/vendor/agent-runtime/dist/thread-reconciler-controller.d.ts +58 -0
- package/vendor/agent-runtime/dist/thread-reconciler-controller.js +137 -0
- package/vendor/agent-runtime/dist/turn-controller.js +2 -2
- package/vendor/agent-runtime/dist/wake-scheduler.d.ts +67 -0
- package/vendor/agent-runtime/dist/wake-scheduler.js +194 -0
- package/vendor/agent-runtime/package.json +8 -1
- package/vendor/pi-web-access/CHANGELOG.md +387 -0
- package/vendor/pi-web-access/LICENSE +21 -0
- package/vendor/pi-web-access/README.md +352 -0
- package/vendor/pi-web-access/activity.ts +101 -0
- package/vendor/pi-web-access/banner.png +0 -0
- package/vendor/pi-web-access/chrome-cookies.ts +322 -0
- package/vendor/pi-web-access/code-search.ts +107 -0
- package/vendor/pi-web-access/curator-page.ts +3359 -0
- package/vendor/pi-web-access/curator-server.ts +605 -0
- package/vendor/pi-web-access/exa.ts +520 -0
- package/vendor/pi-web-access/extract.ts +641 -0
- package/vendor/pi-web-access/gemini-api.ts +112 -0
- package/vendor/pi-web-access/gemini-search.ts +361 -0
- package/vendor/pi-web-access/gemini-url-context.ts +126 -0
- package/vendor/pi-web-access/gemini-web-config.ts +52 -0
- package/vendor/pi-web-access/gemini-web.ts +396 -0
- package/vendor/pi-web-access/github-api.ts +196 -0
- package/vendor/pi-web-access/github-extract.ts +634 -0
- package/vendor/pi-web-access/index.ts +2346 -0
- package/vendor/pi-web-access/package.json +45 -0
- package/vendor/pi-web-access/pdf-extract.ts +192 -0
- package/vendor/pi-web-access/perplexity.ts +195 -0
- package/vendor/pi-web-access/pi-web-fetch-demo.mp4 +0 -0
- package/vendor/pi-web-access/rsc-extract.ts +338 -0
- package/vendor/pi-web-access/skills/librarian/SKILL.md +195 -0
- package/vendor/pi-web-access/storage.ts +72 -0
- package/vendor/pi-web-access/summary-review.ts +276 -0
- package/vendor/pi-web-access/test/gemini-web-cookie-opt-in.test.mjs +41 -0
- package/vendor/pi-web-access/test/pdf-extract.test.mjs +95 -0
- package/vendor/pi-web-access/utils.ts +44 -0
- package/vendor/pi-web-access/video-extract.ts +378 -0
- package/vendor/pi-web-access/youtube-extract.ts +310 -0
- package/dist/lib/pi-adapter/auth.js +0 -68
- package/dist/lib/pi-adapter/auth.js.map +0 -1
- package/dist/lib/pi-adapter/pod-tools.js +0 -140
- package/dist/lib/pi-adapter/pod-tools.js.map +0 -1
- package/dist/skills/drizzle-solid/SKILL.md +0 -340
- package/dist/skills/pod-storage/SKILL.md +0 -100
- package/dist/skills/solid-modeling/SKILL.md +0 -274
- package/dist/skills/xpod-componentsjs/SKILL.md +0 -284
|
@@ -0,0 +1,550 @@
|
|
|
1
|
+
export class LinxSyncOperationError extends Error {
|
|
2
|
+
operationId;
|
|
3
|
+
operationKind;
|
|
4
|
+
constructor(operation, cause) {
|
|
5
|
+
const message = cause instanceof Error ? cause.message : String(cause);
|
|
6
|
+
super(`Sync operation failed (${operation.id}): ${message}`);
|
|
7
|
+
this.name = 'LinxSyncOperationError';
|
|
8
|
+
this.operationId = operation.id;
|
|
9
|
+
this.operationKind = operation.kind;
|
|
10
|
+
this.cause = cause;
|
|
11
|
+
}
|
|
12
|
+
}
|
|
13
|
+
export async function runLinxSyncOperations(operations, options) {
|
|
14
|
+
const baseContext = {
|
|
15
|
+
source: options.source,
|
|
16
|
+
target: options.target,
|
|
17
|
+
direction: options.direction ?? 'local-to-core',
|
|
18
|
+
plane: options.plane ?? 'projection',
|
|
19
|
+
authority: options.authority ?? 'core',
|
|
20
|
+
signal: options.signal,
|
|
21
|
+
now: options.now ?? (() => new Date()),
|
|
22
|
+
metadata: options.metadata,
|
|
23
|
+
};
|
|
24
|
+
const startedAt = baseContext.now().toISOString();
|
|
25
|
+
const result = {
|
|
26
|
+
source: baseContext.source,
|
|
27
|
+
target: baseContext.target,
|
|
28
|
+
direction: baseContext.direction,
|
|
29
|
+
plane: baseContext.plane,
|
|
30
|
+
authority: baseContext.authority,
|
|
31
|
+
attempted: 0,
|
|
32
|
+
applied: 0,
|
|
33
|
+
skipped: 0,
|
|
34
|
+
failed: 0,
|
|
35
|
+
failures: [],
|
|
36
|
+
startedAt,
|
|
37
|
+
completedAt: startedAt,
|
|
38
|
+
status: 'completed',
|
|
39
|
+
metadata: baseContext.metadata,
|
|
40
|
+
};
|
|
41
|
+
for (const operation of operations) {
|
|
42
|
+
throwIfSyncAborted(baseContext.signal);
|
|
43
|
+
const context = resolveOperationContext(baseContext, operation);
|
|
44
|
+
result.attempted += 1;
|
|
45
|
+
const shouldRun = operation.shouldRun ? await operation.shouldRun(context) : true;
|
|
46
|
+
if (!shouldRun) {
|
|
47
|
+
result.skipped += 1;
|
|
48
|
+
await options.onOperationSkipped?.(operation, context);
|
|
49
|
+
continue;
|
|
50
|
+
}
|
|
51
|
+
await options.onOperationStart?.(operation, context);
|
|
52
|
+
try {
|
|
53
|
+
throwIfSyncAborted(context.signal);
|
|
54
|
+
await operation.apply(context);
|
|
55
|
+
throwIfSyncAborted(context.signal);
|
|
56
|
+
result.applied += 1;
|
|
57
|
+
await options.onOperationSuccess?.(operation, context);
|
|
58
|
+
}
|
|
59
|
+
catch (cause) {
|
|
60
|
+
const error = cause instanceof LinxSyncOperationError
|
|
61
|
+
? cause
|
|
62
|
+
: new LinxSyncOperationError(operation, cause);
|
|
63
|
+
result.failed += 1;
|
|
64
|
+
result.failures.push({
|
|
65
|
+
operationId: operation.id,
|
|
66
|
+
message: error.message,
|
|
67
|
+
failedAt: context.now().toISOString(),
|
|
68
|
+
cause: error.cause,
|
|
69
|
+
});
|
|
70
|
+
await options.onOperationError?.(operation, error, context);
|
|
71
|
+
if (!options.continueOnError) {
|
|
72
|
+
finalizeSyncResult(result, context.now().toISOString());
|
|
73
|
+
await writeSyncCheckpoint(options, result);
|
|
74
|
+
throw error;
|
|
75
|
+
}
|
|
76
|
+
}
|
|
77
|
+
}
|
|
78
|
+
finalizeSyncResult(result, baseContext.now().toISOString());
|
|
79
|
+
await writeSyncCheckpoint(options, result);
|
|
80
|
+
return result;
|
|
81
|
+
}
|
|
82
|
+
export async function runLinxSyncTask(options) {
|
|
83
|
+
let failed = false;
|
|
84
|
+
let failure;
|
|
85
|
+
let value;
|
|
86
|
+
const { initialMetadata: taskInitialMetadata, ...runOptions } = options;
|
|
87
|
+
const initialMetadata = typeof options.metadata === 'function' ? taskInitialMetadata : options.metadata;
|
|
88
|
+
const result = await runLinxSyncOperations([
|
|
89
|
+
{
|
|
90
|
+
id: options.operationId,
|
|
91
|
+
kind: options.kind,
|
|
92
|
+
description: options.description,
|
|
93
|
+
apply: async (context) => {
|
|
94
|
+
try {
|
|
95
|
+
value = await options.task(context);
|
|
96
|
+
}
|
|
97
|
+
catch (error) {
|
|
98
|
+
failed = true;
|
|
99
|
+
failure = error;
|
|
100
|
+
throw error;
|
|
101
|
+
}
|
|
102
|
+
},
|
|
103
|
+
},
|
|
104
|
+
], {
|
|
105
|
+
...runOptions,
|
|
106
|
+
metadata: initialMetadata,
|
|
107
|
+
continueOnError: true,
|
|
108
|
+
});
|
|
109
|
+
if (!failed && typeof options.metadata === 'function') {
|
|
110
|
+
result.metadata = options.metadata(value);
|
|
111
|
+
}
|
|
112
|
+
await options.onResult?.(result);
|
|
113
|
+
if (failed) {
|
|
114
|
+
throw failure;
|
|
115
|
+
}
|
|
116
|
+
return { value: value, result };
|
|
117
|
+
}
|
|
118
|
+
export class LinxPodSyncScope {
|
|
119
|
+
options;
|
|
120
|
+
results = [];
|
|
121
|
+
sequence = 0;
|
|
122
|
+
constructor(options) {
|
|
123
|
+
this.options = options;
|
|
124
|
+
}
|
|
125
|
+
getResults() {
|
|
126
|
+
return [...this.results];
|
|
127
|
+
}
|
|
128
|
+
getLastResult() {
|
|
129
|
+
return this.results.length > 0 ? this.results[this.results.length - 1] : null;
|
|
130
|
+
}
|
|
131
|
+
clearResults() {
|
|
132
|
+
this.results.length = 0;
|
|
133
|
+
this.sequence = 0;
|
|
134
|
+
}
|
|
135
|
+
async run(options) {
|
|
136
|
+
const initialMetadata = createLinxPodSyncMetadata({
|
|
137
|
+
action: options.action,
|
|
138
|
+
resourceBindings: resolveInitialResourceBindings(options.resourceBindings, options.refs),
|
|
139
|
+
metadata: {
|
|
140
|
+
...this.options.metadata,
|
|
141
|
+
...(typeof options.metadata === 'function' ? undefined : options.metadata),
|
|
142
|
+
},
|
|
143
|
+
});
|
|
144
|
+
const { value } = await runLinxSyncTask({
|
|
145
|
+
operationId: options.operationId ?? this.nextOperationId(options.action, options.subject),
|
|
146
|
+
kind: options.kind ?? 'custom',
|
|
147
|
+
description: options.description ?? `${options.action}`,
|
|
148
|
+
source: options.source ?? this.options.source,
|
|
149
|
+
target: options.target ?? this.options.target ?? 'pod',
|
|
150
|
+
direction: options.direction ?? this.options.direction ?? 'local-to-core',
|
|
151
|
+
plane: options.plane ?? this.options.plane ?? 'projection',
|
|
152
|
+
authority: options.authority ?? this.options.authority ?? 'core',
|
|
153
|
+
signal: this.options.signal,
|
|
154
|
+
now: this.options.now,
|
|
155
|
+
checkpoint: this.options.checkpoint,
|
|
156
|
+
checkpointId: this.options.checkpointId,
|
|
157
|
+
initialMetadata,
|
|
158
|
+
metadata: (value) => createLinxPodSyncMetadata({
|
|
159
|
+
action: options.action,
|
|
160
|
+
resourceBindings: mergeLinxPodSyncResourceBindings(resolveLinxPodSyncValue(options.refs, value), resolveLinxPodSyncValue(options.resourceBindings, value)),
|
|
161
|
+
metadata: {
|
|
162
|
+
...this.options.metadata,
|
|
163
|
+
...resolveLinxPodSyncValue(options.metadata, value),
|
|
164
|
+
},
|
|
165
|
+
}),
|
|
166
|
+
onOperationStart: this.options.onOperationStart,
|
|
167
|
+
onOperationSuccess: this.options.onOperationSuccess,
|
|
168
|
+
onOperationSkipped: this.options.onOperationSkipped,
|
|
169
|
+
onOperationError: this.options.onOperationError,
|
|
170
|
+
onResult: async (result) => {
|
|
171
|
+
this.results.push(result);
|
|
172
|
+
await this.options.onResult?.(result);
|
|
173
|
+
await options.onResult?.(result);
|
|
174
|
+
},
|
|
175
|
+
task: options.task,
|
|
176
|
+
});
|
|
177
|
+
return value;
|
|
178
|
+
}
|
|
179
|
+
async runOperations(options) {
|
|
180
|
+
const result = await runLinxSyncOperations(options.operations, {
|
|
181
|
+
source: options.source ?? this.options.source,
|
|
182
|
+
target: options.target ?? this.options.target ?? 'pod',
|
|
183
|
+
direction: options.direction ?? this.options.direction ?? 'local-to-core',
|
|
184
|
+
plane: options.plane ?? this.options.plane ?? 'projection',
|
|
185
|
+
authority: options.authority ?? this.options.authority ?? 'core',
|
|
186
|
+
signal: this.options.signal,
|
|
187
|
+
now: this.options.now,
|
|
188
|
+
checkpoint: this.options.checkpoint,
|
|
189
|
+
checkpointId: options.checkpointId ?? this.options.checkpointId,
|
|
190
|
+
continueOnError: options.continueOnError,
|
|
191
|
+
metadata: createLinxPodSyncMetadata({
|
|
192
|
+
action: options.action,
|
|
193
|
+
resourceBindings: mergeLinxPodSyncResourceBindings(options.refs, options.resourceBindings),
|
|
194
|
+
metadata: {
|
|
195
|
+
...this.options.metadata,
|
|
196
|
+
...options.metadata,
|
|
197
|
+
},
|
|
198
|
+
}),
|
|
199
|
+
onOperationStart: this.options.onOperationStart,
|
|
200
|
+
onOperationSuccess: this.options.onOperationSuccess,
|
|
201
|
+
onOperationSkipped: this.options.onOperationSkipped,
|
|
202
|
+
onOperationError: this.options.onOperationError,
|
|
203
|
+
});
|
|
204
|
+
this.results.push(result);
|
|
205
|
+
await this.options.onResult?.(result);
|
|
206
|
+
await options.onResult?.(result);
|
|
207
|
+
return result;
|
|
208
|
+
}
|
|
209
|
+
nextOperationId(action, subject) {
|
|
210
|
+
const timestamp = (this.options.now?.() ?? new Date()).toISOString().replace(/[:.]/g, '-');
|
|
211
|
+
return `${this.options.source}:${action}:${normalizeLinxSyncOperationSegment(subject ?? 'sync')}:${timestamp}:${++this.sequence}`;
|
|
212
|
+
}
|
|
213
|
+
}
|
|
214
|
+
export function createLinxPodSyncScope(options) {
|
|
215
|
+
return new LinxPodSyncScope(options);
|
|
216
|
+
}
|
|
217
|
+
export function createLinxPodSyncMetadata(input) {
|
|
218
|
+
const metadata = {
|
|
219
|
+
action: input.action,
|
|
220
|
+
};
|
|
221
|
+
const resourceBindings = normalizeLinxPodSyncResourceBindings(mergeLinxPodSyncResourceBindings(input.refs, input.resourceBindings));
|
|
222
|
+
if (resourceBindings) {
|
|
223
|
+
metadata.resourceBindings = resourceBindings;
|
|
224
|
+
}
|
|
225
|
+
for (const [key, value] of Object.entries(input.metadata ?? {})) {
|
|
226
|
+
setDefinedMetadataValue(metadata, key, value);
|
|
227
|
+
}
|
|
228
|
+
return metadata;
|
|
229
|
+
}
|
|
230
|
+
export class LinxPodSyncQueue {
|
|
231
|
+
options;
|
|
232
|
+
queue;
|
|
233
|
+
tasks = new Map();
|
|
234
|
+
sequence = 0;
|
|
235
|
+
constructor(options) {
|
|
236
|
+
this.options = options;
|
|
237
|
+
const { onResult, metadata: _metadata, ...queueOptions } = options;
|
|
238
|
+
this.queue = createLinxSyncQueue({
|
|
239
|
+
...queueOptions,
|
|
240
|
+
onResult: async (result, task) => {
|
|
241
|
+
const podTask = this.tasks.get(task.id);
|
|
242
|
+
if (podTask) {
|
|
243
|
+
await onResult?.(result, podTask);
|
|
244
|
+
}
|
|
245
|
+
},
|
|
246
|
+
});
|
|
247
|
+
}
|
|
248
|
+
enqueue(task) {
|
|
249
|
+
const id = task.id ?? this.nextOperationId(task.action, task.subject);
|
|
250
|
+
this.tasks.set(id, task);
|
|
251
|
+
const result = this.queue.enqueue({
|
|
252
|
+
id,
|
|
253
|
+
kind: task.kind ?? 'custom',
|
|
254
|
+
description: task.description ?? task.action,
|
|
255
|
+
source: task.source,
|
|
256
|
+
target: task.target,
|
|
257
|
+
direction: task.direction,
|
|
258
|
+
plane: task.plane,
|
|
259
|
+
authority: task.authority,
|
|
260
|
+
checkpointId: task.checkpointId,
|
|
261
|
+
metadata: createLinxPodSyncMetadata({
|
|
262
|
+
action: task.action,
|
|
263
|
+
resourceBindings: mergeLinxPodSyncResourceBindings(task.refs, task.resourceBindings),
|
|
264
|
+
metadata: {
|
|
265
|
+
...this.options.metadata,
|
|
266
|
+
...task.metadata,
|
|
267
|
+
},
|
|
268
|
+
}),
|
|
269
|
+
run: async (context) => {
|
|
270
|
+
const resolvedRefs = await task.resolveRefs?.(context);
|
|
271
|
+
const resolvedResourceBindings = await task.resolveResourceBindings?.(context);
|
|
272
|
+
const resolvedMetadata = await task.resolveMetadata?.(context);
|
|
273
|
+
if (resolvedRefs || resolvedResourceBindings || resolvedMetadata) {
|
|
274
|
+
Object.assign(context.metadata ?? {}, createLinxPodSyncMetadata({
|
|
275
|
+
action: task.action,
|
|
276
|
+
resourceBindings: mergeLinxPodSyncResourceBindings(mergeLinxPodSyncResourceBindings(task.refs, task.resourceBindings), mergeLinxPodSyncResourceBindings(resolvedRefs, resolvedResourceBindings)),
|
|
277
|
+
metadata: {
|
|
278
|
+
...this.options.metadata,
|
|
279
|
+
...task.metadata,
|
|
280
|
+
...resolvedMetadata,
|
|
281
|
+
},
|
|
282
|
+
}));
|
|
283
|
+
}
|
|
284
|
+
await task.run(context);
|
|
285
|
+
},
|
|
286
|
+
});
|
|
287
|
+
void result.finally(() => {
|
|
288
|
+
this.tasks.delete(id);
|
|
289
|
+
});
|
|
290
|
+
return result;
|
|
291
|
+
}
|
|
292
|
+
async flush() {
|
|
293
|
+
await this.queue.flush();
|
|
294
|
+
}
|
|
295
|
+
getResults() {
|
|
296
|
+
return this.queue.getResults();
|
|
297
|
+
}
|
|
298
|
+
nextOperationId(action, subject) {
|
|
299
|
+
const timestamp = (this.options.now?.() ?? new Date()).toISOString().replace(/[:.]/g, '-');
|
|
300
|
+
return `${this.options.source}:${action}:${normalizeLinxSyncOperationSegment(subject ?? 'sync')}:${timestamp}:${++this.sequence}`;
|
|
301
|
+
}
|
|
302
|
+
}
|
|
303
|
+
export function createLinxPodSyncQueue(options) {
|
|
304
|
+
return new LinxPodSyncQueue(options);
|
|
305
|
+
}
|
|
306
|
+
export class LinxSyncQueue {
|
|
307
|
+
options;
|
|
308
|
+
tail = Promise.resolve();
|
|
309
|
+
results = [];
|
|
310
|
+
constructor(options) {
|
|
311
|
+
this.options = options;
|
|
312
|
+
}
|
|
313
|
+
enqueue(task) {
|
|
314
|
+
const operation = {
|
|
315
|
+
id: task.id,
|
|
316
|
+
kind: task.kind ?? 'custom',
|
|
317
|
+
description: task.description,
|
|
318
|
+
source: task.source,
|
|
319
|
+
target: task.target,
|
|
320
|
+
direction: task.direction,
|
|
321
|
+
plane: task.plane,
|
|
322
|
+
authority: task.authority,
|
|
323
|
+
apply: task.run,
|
|
324
|
+
};
|
|
325
|
+
const next = this.tail
|
|
326
|
+
.then(() => this.runTask(task, operation), () => this.runTask(task, operation))
|
|
327
|
+
.catch((error) => {
|
|
328
|
+
this.options.onError?.(error);
|
|
329
|
+
return null;
|
|
330
|
+
});
|
|
331
|
+
this.tail = next;
|
|
332
|
+
return next;
|
|
333
|
+
}
|
|
334
|
+
async flush() {
|
|
335
|
+
await this.tail;
|
|
336
|
+
}
|
|
337
|
+
getResults() {
|
|
338
|
+
return [...this.results];
|
|
339
|
+
}
|
|
340
|
+
async runTask(task, operation) {
|
|
341
|
+
const result = await runLinxSyncOperations([operation], {
|
|
342
|
+
...this.options,
|
|
343
|
+
source: task.source ?? this.options.source,
|
|
344
|
+
target: task.target ?? this.options.target,
|
|
345
|
+
direction: task.direction ?? this.options.direction,
|
|
346
|
+
plane: task.plane ?? this.options.plane,
|
|
347
|
+
authority: task.authority ?? this.options.authority,
|
|
348
|
+
checkpointId: task.checkpointId ?? task.id,
|
|
349
|
+
metadata: {
|
|
350
|
+
...this.options.metadata,
|
|
351
|
+
...task.metadata,
|
|
352
|
+
syncTask: task.id,
|
|
353
|
+
...(task.description ? { syncTaskDescription: task.description } : {}),
|
|
354
|
+
},
|
|
355
|
+
});
|
|
356
|
+
this.results.push(result);
|
|
357
|
+
await this.options.onResult?.(result, task);
|
|
358
|
+
return result;
|
|
359
|
+
}
|
|
360
|
+
}
|
|
361
|
+
export function createLinxSyncQueue(options) {
|
|
362
|
+
return new LinxSyncQueue(options);
|
|
363
|
+
}
|
|
364
|
+
export function createInMemoryLinxSyncCheckpointStore(initial = []) {
|
|
365
|
+
const checkpoints = new Map();
|
|
366
|
+
for (const checkpoint of initial) {
|
|
367
|
+
checkpoints.set(checkpoint.id, checkpoint);
|
|
368
|
+
}
|
|
369
|
+
return {
|
|
370
|
+
writeCheckpoint(checkpoint) {
|
|
371
|
+
checkpoints.set(checkpoint.id, checkpoint);
|
|
372
|
+
},
|
|
373
|
+
readCheckpoint(id) {
|
|
374
|
+
return checkpoints.get(id) ?? null;
|
|
375
|
+
},
|
|
376
|
+
listCheckpoints(query) {
|
|
377
|
+
return [...checkpoints.values()].filter((checkpoint) => matchesLinxSyncCheckpointQuery(checkpoint, query));
|
|
378
|
+
},
|
|
379
|
+
deleteCheckpoint(id) {
|
|
380
|
+
checkpoints.delete(id);
|
|
381
|
+
},
|
|
382
|
+
};
|
|
383
|
+
}
|
|
384
|
+
export function createLinxSyncCheckpoint(id, result, metadata) {
|
|
385
|
+
return {
|
|
386
|
+
id,
|
|
387
|
+
source: result.source,
|
|
388
|
+
target: result.target,
|
|
389
|
+
direction: result.direction,
|
|
390
|
+
plane: result.plane,
|
|
391
|
+
authority: result.authority,
|
|
392
|
+
status: result.status,
|
|
393
|
+
attempted: result.attempted,
|
|
394
|
+
applied: result.applied,
|
|
395
|
+
skipped: result.skipped,
|
|
396
|
+
failed: result.failed,
|
|
397
|
+
failures: result.failures,
|
|
398
|
+
startedAt: result.startedAt,
|
|
399
|
+
completedAt: result.completedAt,
|
|
400
|
+
metadata,
|
|
401
|
+
};
|
|
402
|
+
}
|
|
403
|
+
export async function readLinxSyncCheckpoint(store, id) {
|
|
404
|
+
return store.readCheckpoint ? await store.readCheckpoint(id) : null;
|
|
405
|
+
}
|
|
406
|
+
export async function listLinxSyncCheckpoints(store, query) {
|
|
407
|
+
if (store.listCheckpoints) {
|
|
408
|
+
return store.listCheckpoints(query);
|
|
409
|
+
}
|
|
410
|
+
return [];
|
|
411
|
+
}
|
|
412
|
+
export async function deleteLinxSyncCheckpoint(store, id) {
|
|
413
|
+
await store.deleteCheckpoint?.(id);
|
|
414
|
+
}
|
|
415
|
+
export function matchesLinxSyncCheckpointQuery(checkpoint, query = {}) {
|
|
416
|
+
if (query.source !== undefined && checkpoint.source !== query.source)
|
|
417
|
+
return false;
|
|
418
|
+
if (query.target !== undefined && checkpoint.target !== query.target)
|
|
419
|
+
return false;
|
|
420
|
+
if (query.direction !== undefined && checkpoint.direction !== query.direction)
|
|
421
|
+
return false;
|
|
422
|
+
if (query.plane !== undefined && checkpoint.plane !== query.plane)
|
|
423
|
+
return false;
|
|
424
|
+
if (query.authority !== undefined && checkpoint.authority !== query.authority)
|
|
425
|
+
return false;
|
|
426
|
+
if (query.status !== undefined) {
|
|
427
|
+
const statuses = Array.isArray(query.status) ? query.status : [query.status];
|
|
428
|
+
if (!statuses.includes(checkpoint.status))
|
|
429
|
+
return false;
|
|
430
|
+
}
|
|
431
|
+
if (query.metadata) {
|
|
432
|
+
for (const [key, value] of Object.entries(query.metadata)) {
|
|
433
|
+
if (!matchesLinxSyncMetadataValue(readLinxSyncMetadataValue(checkpoint.metadata, key), value))
|
|
434
|
+
return false;
|
|
435
|
+
}
|
|
436
|
+
}
|
|
437
|
+
return true;
|
|
438
|
+
}
|
|
439
|
+
function readLinxSyncMetadataValue(metadata, key) {
|
|
440
|
+
if (key === 'resourceBindings') {
|
|
441
|
+
return metadata?.resourceBindings ?? metadata?.refs;
|
|
442
|
+
}
|
|
443
|
+
if (key === 'refs') {
|
|
444
|
+
return metadata?.refs ?? metadata?.resourceBindings;
|
|
445
|
+
}
|
|
446
|
+
return metadata?.[key];
|
|
447
|
+
}
|
|
448
|
+
function matchesLinxSyncMetadataValue(actual, expected) {
|
|
449
|
+
if (expected && typeof expected === 'object' && !Array.isArray(expected)) {
|
|
450
|
+
if (!actual || typeof actual !== 'object' || Array.isArray(actual)) {
|
|
451
|
+
return false;
|
|
452
|
+
}
|
|
453
|
+
const actualRecord = actual;
|
|
454
|
+
return Object.entries(expected)
|
|
455
|
+
.every(([key, value]) => matchesLinxSyncMetadataValue(actualRecord[key], value));
|
|
456
|
+
}
|
|
457
|
+
return actual === expected;
|
|
458
|
+
}
|
|
459
|
+
export function isPendingLinxSyncCheckpoint(checkpoint) {
|
|
460
|
+
return checkpoint.status === 'failed' || checkpoint.status === 'partial';
|
|
461
|
+
}
|
|
462
|
+
function finalizeSyncResult(result, completedAt) {
|
|
463
|
+
result.completedAt = completedAt;
|
|
464
|
+
result.status = result.failed === 0
|
|
465
|
+
? 'completed'
|
|
466
|
+
: result.applied > 0 || result.skipped > 0
|
|
467
|
+
? 'partial'
|
|
468
|
+
: 'failed';
|
|
469
|
+
}
|
|
470
|
+
async function writeSyncCheckpoint(options, result) {
|
|
471
|
+
if (!options.checkpoint) {
|
|
472
|
+
return;
|
|
473
|
+
}
|
|
474
|
+
const checkpointId = typeof options.checkpointId === 'function'
|
|
475
|
+
? options.checkpointId(result)
|
|
476
|
+
: options.checkpointId ?? `${result.source}:${result.target}:${result.plane}`;
|
|
477
|
+
await options.checkpoint.writeCheckpoint(createLinxSyncCheckpoint(checkpointId, result, options.metadata));
|
|
478
|
+
}
|
|
479
|
+
function resolveOperationContext(base, operation) {
|
|
480
|
+
return {
|
|
481
|
+
...base,
|
|
482
|
+
source: operation.source ?? base.source,
|
|
483
|
+
target: operation.target ?? base.target,
|
|
484
|
+
direction: operation.direction ?? base.direction,
|
|
485
|
+
plane: operation.plane ?? base.plane,
|
|
486
|
+
authority: operation.authority ?? base.authority,
|
|
487
|
+
};
|
|
488
|
+
}
|
|
489
|
+
function resolveLinxPodSyncValue(value, input) {
|
|
490
|
+
return typeof value === 'function'
|
|
491
|
+
? value(input)
|
|
492
|
+
: value;
|
|
493
|
+
}
|
|
494
|
+
function resolveInitialResourceBindings(resourceBindings, refs) {
|
|
495
|
+
return mergeLinxPodSyncResourceBindings(typeof refs === 'function' ? undefined : refs, typeof resourceBindings === 'function' ? undefined : resourceBindings);
|
|
496
|
+
}
|
|
497
|
+
function mergeLinxPodSyncResourceBindings(base, override) {
|
|
498
|
+
if (!base && !override) {
|
|
499
|
+
return undefined;
|
|
500
|
+
}
|
|
501
|
+
const merged = { ...(base ?? {}) };
|
|
502
|
+
for (const [name, ref] of Object.entries(override ?? {})) {
|
|
503
|
+
if (!ref) {
|
|
504
|
+
merged[name] = ref;
|
|
505
|
+
continue;
|
|
506
|
+
}
|
|
507
|
+
merged[name] = {
|
|
508
|
+
...(merged[name] ?? {}),
|
|
509
|
+
...ref,
|
|
510
|
+
};
|
|
511
|
+
}
|
|
512
|
+
return merged;
|
|
513
|
+
}
|
|
514
|
+
function normalizeLinxPodSyncResourceBindings(resourceBindings) {
|
|
515
|
+
const normalized = {};
|
|
516
|
+
for (const [name, ref] of Object.entries(resourceBindings ?? {})) {
|
|
517
|
+
if (!ref)
|
|
518
|
+
continue;
|
|
519
|
+
const next = {};
|
|
520
|
+
if (ref.uri !== undefined && ref.uri !== null) {
|
|
521
|
+
next.uri = ref.uri;
|
|
522
|
+
}
|
|
523
|
+
if (ref.local !== undefined && ref.local !== null) {
|
|
524
|
+
next.local = ref.local;
|
|
525
|
+
}
|
|
526
|
+
if (Object.keys(next).length > 0) {
|
|
527
|
+
normalized[name] = next;
|
|
528
|
+
}
|
|
529
|
+
}
|
|
530
|
+
return Object.keys(normalized).length > 0 ? normalized : undefined;
|
|
531
|
+
}
|
|
532
|
+
function setDefinedMetadataValue(metadata, key, value) {
|
|
533
|
+
if (value !== undefined && value !== null) {
|
|
534
|
+
metadata[key] = value;
|
|
535
|
+
}
|
|
536
|
+
}
|
|
537
|
+
function normalizeLinxSyncOperationSegment(value) {
|
|
538
|
+
return value.replace(/[:\s]+/g, '-');
|
|
539
|
+
}
|
|
540
|
+
function throwIfSyncAborted(signal) {
|
|
541
|
+
if (!signal?.aborted) {
|
|
542
|
+
return;
|
|
543
|
+
}
|
|
544
|
+
if (signal.reason instanceof Error) {
|
|
545
|
+
throw signal.reason;
|
|
546
|
+
}
|
|
547
|
+
const error = new Error('The sync operation was aborted.');
|
|
548
|
+
error.name = 'AbortError';
|
|
549
|
+
throw error;
|
|
550
|
+
}
|
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
import { type ReconcileDecision, type ReconcileDecisionSummary, type ReconcileThreadEventInput, type ThreadControlEvent, type ThreadPolicy, type ThreadPolicyKind, type WakeJob } from './reconciler.js';
|
|
2
|
+
import { type WakeJobExecutionRecord, type WakeJobExecutionRecordSummary, type WakeJobSchedulerSnapshot, type WakeJobSchedulerSnapshotSummary } from './wake-scheduler.js';
|
|
3
|
+
type MaybePromise<T> = T | Promise<T>;
|
|
4
|
+
export interface ThreadWakeJobContext {
|
|
5
|
+
job: WakeJob;
|
|
6
|
+
record: WakeJobExecutionRecord;
|
|
7
|
+
decision: ReconcileDecision;
|
|
8
|
+
decisionSummary: ReconcileDecisionSummary;
|
|
9
|
+
}
|
|
10
|
+
export type ThreadWakeJobHandler = (context: ThreadWakeJobContext) => MaybePromise<unknown>;
|
|
11
|
+
export interface ThreadReconcilerControllerOptions {
|
|
12
|
+
policy: ThreadPolicyKind | ThreadPolicy;
|
|
13
|
+
handleWakeJob: ThreadWakeJobHandler;
|
|
14
|
+
concurrency?: number;
|
|
15
|
+
autoStart?: boolean;
|
|
16
|
+
now?: () => Date;
|
|
17
|
+
onDecision?: (decision: ReconcileDecisionSummary) => void;
|
|
18
|
+
onWakeJobQueued?: (record: WakeJobExecutionRecord, decision: ReconcileDecisionSummary) => void;
|
|
19
|
+
onWakeJobStarted?: (record: WakeJobExecutionRecord, decision: ReconcileDecisionSummary) => void;
|
|
20
|
+
onWakeJobCompleted?: (record: WakeJobExecutionRecord, decision: ReconcileDecisionSummary) => void;
|
|
21
|
+
onWakeJobFailed?: (record: WakeJobExecutionRecord, decision: ReconcileDecisionSummary) => void;
|
|
22
|
+
}
|
|
23
|
+
export interface ThreadReconcilerDispatchOptions extends Omit<ReconcileThreadEventInput, 'policy' | 'event'> {
|
|
24
|
+
}
|
|
25
|
+
export interface ThreadReconcilerDispatchResult {
|
|
26
|
+
decision: ReconcileDecision;
|
|
27
|
+
summary: ReconcileDecisionSummary;
|
|
28
|
+
wakeRecords: WakeJobExecutionRecord[];
|
|
29
|
+
wakeRecordSummaries: WakeJobExecutionRecordSummary[];
|
|
30
|
+
}
|
|
31
|
+
export interface ThreadReconcilerDrainResult {
|
|
32
|
+
scheduler: WakeJobSchedulerSnapshot;
|
|
33
|
+
schedulerSummary: WakeJobSchedulerSnapshotSummary;
|
|
34
|
+
}
|
|
35
|
+
export interface ThreadReconcilerDispatchAndDrainResult extends ThreadReconcilerDispatchResult, ThreadReconcilerDrainResult {
|
|
36
|
+
}
|
|
37
|
+
export interface ThreadReconcilerDecisionResult {
|
|
38
|
+
decision: ReconcileDecision;
|
|
39
|
+
summary: ReconcileDecisionSummary;
|
|
40
|
+
}
|
|
41
|
+
export interface RunThreadReconcilerCycleOptions extends Omit<ThreadReconcilerControllerOptions, 'autoStart'> {
|
|
42
|
+
event: ThreadControlEvent;
|
|
43
|
+
dispatchOptions?: ThreadReconcilerDispatchOptions;
|
|
44
|
+
onDispatched?: (result: ThreadReconcilerDispatchResult) => MaybePromise<void>;
|
|
45
|
+
}
|
|
46
|
+
export interface ThreadReconcilerController {
|
|
47
|
+
dispatch(event: ThreadControlEvent, options?: ThreadReconcilerDispatchOptions): ThreadReconcilerDispatchResult;
|
|
48
|
+
dispatchAndDrain(event: ThreadControlEvent, options?: ThreadReconcilerDispatchOptions): Promise<ThreadReconcilerDispatchAndDrainResult>;
|
|
49
|
+
startAndDrain(): Promise<ThreadReconcilerDrainResult>;
|
|
50
|
+
start(): void;
|
|
51
|
+
stop(): void;
|
|
52
|
+
drain(): Promise<void>;
|
|
53
|
+
snapshot(): WakeJobSchedulerSnapshot;
|
|
54
|
+
}
|
|
55
|
+
export declare function createThreadReconcilerController(options: ThreadReconcilerControllerOptions): ThreadReconcilerController;
|
|
56
|
+
export declare function decideThreadControlEvent(input: ReconcileThreadEventInput): ThreadReconcilerDecisionResult;
|
|
57
|
+
export declare function runThreadReconcilerCycle(options: RunThreadReconcilerCycleOptions): Promise<ThreadReconcilerDispatchAndDrainResult>;
|
|
58
|
+
export {};
|