@opengeni/codemode 0.2.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.
@@ -0,0 +1,11 @@
1
+ import type { AttemptToolResult } from "@opengeni/contracts";
2
+ import type { CodemodeCallOptions, CodemodeClient } from "./index.js";
3
+ import { type CodemodeClientProvider } from "./environment.js";
4
+ export declare class CodemodeToolExecutionError extends Error {
5
+ readonly result: AttemptToolResult;
6
+ readonly code: string;
7
+ readonly retryable: boolean;
8
+ constructor(result: AttemptToolResult);
9
+ }
10
+ export declare function codemodeClientProvider(client?: CodemodeClient | CodemodeClientProvider): CodemodeClientProvider;
11
+ export declare function callStructured<T>(client: CodemodeClientProvider, path: readonly string[], args: Record<string, unknown>, options: CodemodeCallOptions): Promise<T>;
package/package.json ADDED
@@ -0,0 +1,43 @@
1
+ {
2
+ "name": "@opengeni/codemode",
3
+ "version": "0.2.2",
4
+ "description": "Attempt-frozen programmatic tool catalog and execution authority for OpenGeni.",
5
+ "license": "Apache-2.0",
6
+ "repository": {
7
+ "type": "git",
8
+ "url": "git+https://github.com/Cloudgeni-ai/opengeni.git",
9
+ "directory": "packages/codemode"
10
+ },
11
+ "files": [
12
+ "dist",
13
+ "src",
14
+ "README.md"
15
+ ],
16
+ "type": "module",
17
+ "sideEffects": false,
18
+ "main": "./dist/index.js",
19
+ "module": "./dist/index.js",
20
+ "types": "./dist/index.d.ts",
21
+ "exports": {
22
+ ".": {
23
+ "types": "./dist/index.d.ts",
24
+ "import": "./dist/index.js"
25
+ }
26
+ },
27
+ "publishConfig": {
28
+ "access": "public",
29
+ "provenance": true
30
+ },
31
+ "scripts": {
32
+ "build": "bun ../../scripts/build-typescript-package.ts",
33
+ "typecheck": "tsc --noEmit",
34
+ "prepublishOnly": "bash ../../scripts/prepublish-guard"
35
+ },
36
+ "dependencies": {
37
+ "@opengeni/contracts": "^0.44.1",
38
+ "ajv": "^8.20.0"
39
+ },
40
+ "engines": {
41
+ "node": ">=18"
42
+ }
43
+ }
@@ -0,0 +1,313 @@
1
+ import { randomBytes } from "node:crypto";
2
+
3
+ import type {
4
+ DocumentArtifactId,
5
+ DocumentArtifactIdKind,
6
+ DocumentArtifactCommand,
7
+ DocumentArtifactQuery,
8
+ EditableArtifactStableId,
9
+ PresentationArtifactCommand,
10
+ PresentationArtifactQuery,
11
+ SpreadsheetArtifactCommand,
12
+ SpreadsheetArtifactKernelQuery,
13
+ } from "@opengeni/contracts/editable-artifacts";
14
+ import type { CodemodeCallOptions } from "./index";
15
+ import type { CodemodeClientProvider } from "./environment";
16
+ import { callStructured } from "./structured";
17
+
18
+ const PATH = {
19
+ list: ["artifacts", "list"],
20
+ create: ["artifacts", "create"],
21
+ import: ["artifacts", "import"],
22
+ get: ["artifacts", "get"],
23
+ inspect: ["artifacts", "inspect"],
24
+ apply: ["artifacts", "apply"],
25
+ export: ["artifacts", "export"],
26
+ exportStatus: ["artifacts", "exportStatus"],
27
+ } as const;
28
+
29
+ export type CodemodeArtifactModality = "spreadsheet" | "document" | "presentation";
30
+ export type CodemodeArtifactFormat = "xlsx" | "pptx" | "docx" | "pdf" | "png" | "webp";
31
+ export type CodemodeArtifactMetadata = Readonly<{
32
+ id: string;
33
+ modality: CodemodeArtifactModality;
34
+ title: string;
35
+ lifecycle: "active" | "archived";
36
+ headSequence: number;
37
+ stateHash: string;
38
+ createdAt: string;
39
+ updatedAt: string;
40
+ }>;
41
+ export type CodemodeArtifactQuery =
42
+ | SpreadsheetArtifactKernelQuery
43
+ | DocumentArtifactQuery
44
+ | PresentationArtifactQuery;
45
+ export type CodemodeArtifactCommand =
46
+ | SpreadsheetArtifactCommand
47
+ | DocumentArtifactCommand
48
+ | PresentationArtifactCommand;
49
+ export type CodemodeArtifactMutationReceipt = Readonly<{
50
+ artifact: CodemodeArtifactMetadata;
51
+ transaction: Readonly<{
52
+ id: string;
53
+ clientTransactionId: string;
54
+ sequenceStart: number;
55
+ sequenceEnd: number;
56
+ stateHash: string;
57
+ committedAt: string;
58
+ replayed: boolean;
59
+ }>;
60
+ }>;
61
+ export type CodemodeArtifactExportFile = Readonly<{
62
+ fileId: string;
63
+ filename: string;
64
+ contentType: string;
65
+ sizeBytes: number;
66
+ sha256: string;
67
+ artifactId: string;
68
+ versionId: string;
69
+ materializationJobId: string;
70
+ sourceHeadSequence: number;
71
+ sourceStateHash: string;
72
+ }>;
73
+ export type CodemodeArtifactExportStatus = Readonly<{
74
+ artifact: CodemodeArtifactMetadata;
75
+ versionId: string;
76
+ jobId: string;
77
+ sourceHeadSequence: number;
78
+ sourceStateHash: string;
79
+ state: "pending" | "running" | "succeeded" | "failed";
80
+ errorCode: string | null;
81
+ file: CodemodeArtifactExportFile | null;
82
+ }>;
83
+
84
+ export type CodemodeArtifactIdFactory = Readonly<{
85
+ /** Create a nonzero 128-bit id accepted by spreadsheet and presentation commands. */
86
+ stable(): EditableArtifactStableId;
87
+ /** Create a document object id in the namespace returned by a summary query. */
88
+ document(kind: DocumentArtifactIdKind, namespace: bigint | number | string): DocumentArtifactId;
89
+ }>;
90
+
91
+ export const codemodeArtifactIds: CodemodeArtifactIdFactory = Object.freeze({
92
+ stable: createStableArtifactId,
93
+ document: createDocumentArtifactId,
94
+ });
95
+
96
+ /** Authored CodeMode collection over the exact MCP-backed artifact operations. */
97
+ export class CodemodeArtifactCollection {
98
+ readonly ids = codemodeArtifactIds;
99
+
100
+ constructor(private readonly client: CodemodeClientProvider) {}
101
+
102
+ async list(
103
+ options: { limit?: number | undefined } = {},
104
+ callOptions: CodemodeCallOptions = {},
105
+ ): Promise<CodemodeArtifactMetadata[]> {
106
+ return (
107
+ await callStructured<{ artifacts: CodemodeArtifactMetadata[] }>(
108
+ this.client,
109
+ PATH.list,
110
+ options,
111
+ callOptions,
112
+ )
113
+ ).artifacts;
114
+ }
115
+
116
+ async create(
117
+ modality: CodemodeArtifactModality,
118
+ title: string,
119
+ callOptions: CodemodeCallOptions = {},
120
+ ): Promise<CodemodeArtifact> {
121
+ const metadata = await callStructured<CodemodeArtifactMetadata>(
122
+ this.client,
123
+ PATH.create,
124
+ { modality, title },
125
+ callOptions,
126
+ );
127
+ return new CodemodeArtifact(this.client, metadata.id, metadata);
128
+ }
129
+
130
+ async import(
131
+ fileId: string,
132
+ modality: CodemodeArtifactModality,
133
+ title: string,
134
+ callOptions: CodemodeCallOptions = {},
135
+ ): Promise<CodemodeArtifact> {
136
+ const metadata = await callStructured<CodemodeArtifactMetadata>(
137
+ this.client,
138
+ PATH.import,
139
+ { fileId, modality, title },
140
+ callOptions,
141
+ );
142
+ return new CodemodeArtifact(this.client, metadata.id, metadata);
143
+ }
144
+
145
+ use(artifact: string | CodemodeArtifactMetadata): CodemodeArtifact {
146
+ return typeof artifact === "string"
147
+ ? new CodemodeArtifact(this.client, artifact)
148
+ : new CodemodeArtifact(this.client, artifact.id, artifact);
149
+ }
150
+ }
151
+
152
+ const DOCUMENT_ID_PREFIX = Object.freeze({
153
+ paragraph: "p",
154
+ table: "dt",
155
+ "page-break": "pb",
156
+ section: "sec",
157
+ header: "hdr",
158
+ footer: "ftr",
159
+ comment: "dc",
160
+ "tracked-change": "chg",
161
+ } satisfies Readonly<Record<DocumentArtifactIdKind, string>>);
162
+ const MAX_U64 = 0xffff_ffff_ffff_ffffn;
163
+ const MAX_DOCUMENT_COUNTER = BigInt(Number.MAX_SAFE_INTEGER) - 1n;
164
+
165
+ function createStableArtifactId(): EditableArtifactStableId {
166
+ const bytes = randomBytes(16);
167
+ if (bytes.subarray(0, 8).every((byte) => byte === 0)) bytes[0] = 1;
168
+ if (bytes.subarray(8, 16).every((byte) => byte === 0)) bytes[8] = 1;
169
+ return bytes.toString("hex") as EditableArtifactStableId;
170
+ }
171
+
172
+ function createDocumentArtifactId(
173
+ kind: DocumentArtifactIdKind,
174
+ namespaceInput: bigint | number | string,
175
+ ): DocumentArtifactId {
176
+ const namespace = parseDocumentNamespace(namespaceInput);
177
+ const bytes = randomBytes(7);
178
+ bytes[0] = (bytes[0] ?? 0) & 0x1f;
179
+ let counter = 0n;
180
+ for (const byte of bytes) counter = (counter << 8n) | BigInt(byte);
181
+ if (counter === 0n) counter = 1n;
182
+ if (counter > MAX_DOCUMENT_COUNTER) {
183
+ throw new Error("Generated document id counter exceeds the canonical limit");
184
+ }
185
+ return `${DOCUMENT_ID_PREFIX[kind]}/${namespace.toString(16).padStart(16, "0")}${counter
186
+ .toString(16)
187
+ .padStart(16, "0")}`;
188
+ }
189
+
190
+ function parseDocumentNamespace(input: bigint | number | string): bigint {
191
+ if (
192
+ (typeof input === "number" && (!Number.isSafeInteger(input) || input < 0)) ||
193
+ (typeof input === "string" && !/^(?:0|[1-9][0-9]*)$/u.test(input))
194
+ ) {
195
+ throw new TypeError("Document id namespace must be an unsigned decimal integer");
196
+ }
197
+ const namespace = BigInt(input);
198
+ if (namespace < 0n || namespace > MAX_U64) {
199
+ throw new RangeError("Document id namespace is outside the uint64 range");
200
+ }
201
+ return namespace;
202
+ }
203
+
204
+ export class CodemodeArtifact {
205
+ private metadata: CodemodeArtifactMetadata | null;
206
+
207
+ constructor(
208
+ private readonly client: CodemodeClientProvider,
209
+ readonly id: string,
210
+ metadata: CodemodeArtifactMetadata | null = null,
211
+ ) {
212
+ this.metadata = metadata;
213
+ }
214
+
215
+ async get(callOptions: CodemodeCallOptions = {}): Promise<CodemodeArtifactMetadata> {
216
+ const metadata = await callStructured<CodemodeArtifactMetadata>(
217
+ this.client,
218
+ PATH.get,
219
+ { artifactId: this.id },
220
+ callOptions,
221
+ );
222
+ this.metadata = metadata;
223
+ return metadata;
224
+ }
225
+
226
+ async inspect<T = unknown>(
227
+ query: CodemodeArtifactQuery,
228
+ callOptions: CodemodeCallOptions = {},
229
+ ): Promise<Readonly<{ artifact: CodemodeArtifactMetadata; projection: T }>> {
230
+ const modality = (await this.currentMetadata(callOptions)).modality;
231
+ const result = await callStructured<{
232
+ artifact: CodemodeArtifactMetadata;
233
+ projection: T;
234
+ }>(this.client, PATH.inspect, { artifactId: this.id, modality, request: query }, callOptions);
235
+ this.metadata = result.artifact;
236
+ return result;
237
+ }
238
+
239
+ async apply(
240
+ commands: readonly CodemodeArtifactCommand[],
241
+ callOptions: CodemodeCallOptions = {},
242
+ ): Promise<CodemodeArtifactMutationReceipt> {
243
+ const metadata = await this.currentMetadata(callOptions);
244
+ const result = await callStructured<CodemodeArtifactMutationReceipt>(
245
+ this.client,
246
+ PATH.apply,
247
+ {
248
+ artifactId: this.id,
249
+ modality: metadata.modality,
250
+ expectedHeadSequence: metadata.headSequence,
251
+ expectedStateHash: metadata.stateHash,
252
+ commands,
253
+ },
254
+ callOptions,
255
+ );
256
+ this.metadata = result.artifact;
257
+ return result;
258
+ }
259
+
260
+ async export(
261
+ format: CodemodeArtifactFormat,
262
+ options: Readonly<Record<string, unknown>> = {},
263
+ callOptions: CodemodeCallOptions = {},
264
+ ): Promise<CodemodeArtifactExport> {
265
+ const started = await callStructured<{
266
+ artifact: CodemodeArtifactMetadata;
267
+ versionId: string;
268
+ jobId: string;
269
+ sourceHeadSequence: number;
270
+ sourceStateHash: string;
271
+ state: "pending" | "running" | "succeeded" | "failed";
272
+ }>(this.client, PATH.export, { artifactId: this.id, format, options }, callOptions);
273
+ this.metadata = started.artifact;
274
+ return new CodemodeArtifactExport(
275
+ this.client,
276
+ started.artifact.id,
277
+ started.versionId,
278
+ started.jobId,
279
+ started.sourceHeadSequence,
280
+ started.sourceStateHash,
281
+ );
282
+ }
283
+
284
+ private async currentMetadata(
285
+ callOptions: CodemodeCallOptions,
286
+ ): Promise<CodemodeArtifactMetadata> {
287
+ return this.metadata ?? (await this.get(callOptions));
288
+ }
289
+ }
290
+
291
+ export class CodemodeArtifactExport {
292
+ constructor(
293
+ private readonly client: CodemodeClientProvider,
294
+ readonly artifactId: string,
295
+ readonly versionId: string,
296
+ readonly jobId: string,
297
+ readonly sourceHeadSequence: number,
298
+ readonly sourceStateHash: string,
299
+ ) {}
300
+
301
+ async status(callOptions: CodemodeCallOptions = {}): Promise<CodemodeArtifactExportStatus> {
302
+ return await callStructured(
303
+ this.client,
304
+ PATH.exportStatus,
305
+ {
306
+ artifactId: this.artifactId,
307
+ versionId: this.versionId,
308
+ jobId: this.jobId,
309
+ },
310
+ callOptions,
311
+ );
312
+ }
313
+ }