@bpmsoftwaresolutions/ai-engine-client 1.0.0-beta.1 → 1.0.0-beta.11
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 +270 -5
- package/examples/implementation-plan-packet.minimal.json +76 -0
- package/package.json +32 -28
- package/src/index.js +441 -10
package/README.md
CHANGED
|
@@ -10,13 +10,25 @@ No repo clone required. Install the package, point it at the Azure service, and
|
|
|
10
10
|
npm install @bpmsoftwaresolutions/ai-engine-client
|
|
11
11
|
```
|
|
12
12
|
|
|
13
|
+
## Publishing
|
|
14
|
+
|
|
15
|
+
This package is published automatically from GitHub Actions by `.github/workflows/publish-ai-engine-client.yml`.
|
|
16
|
+
|
|
17
|
+
The workflow runs on pushes to `main` when files under `packages/ai-engine-client/**` change, and it can also be started manually with `workflow_dispatch`.
|
|
18
|
+
|
|
19
|
+
Authentication supports either:
|
|
20
|
+
|
|
21
|
+
- npm trusted publishing
|
|
22
|
+
- a repository secret named `NPM_TOKEN`, `NPM_PUBLISH_TOKEN`, or `NODE_AUTH_TOKEN`
|
|
23
|
+
|
|
13
24
|
## Quick Start
|
|
14
25
|
|
|
15
26
|
```js
|
|
16
27
|
import { AIEngineClient } from '@bpmsoftwaresolutions/ai-engine-client';
|
|
17
28
|
|
|
18
29
|
const client = new AIEngineClient({
|
|
19
|
-
baseUrl: 'https://
|
|
30
|
+
baseUrl: 'https://55e8b585-6076-4992-a164-a5398899efdb-00-c56xlmp9cuq9.worf.replit.dev',
|
|
31
|
+
// pass the key via a custom header or interceptor
|
|
20
32
|
});
|
|
21
33
|
|
|
22
34
|
// Health
|
|
@@ -38,13 +50,72 @@ const charter = await client.createProjectCharter({
|
|
|
38
50
|
|
|
39
51
|
// Read the resulting project roadmap
|
|
40
52
|
const roadmap = await client.getProjectRoadmap(charter.project_id);
|
|
53
|
+
|
|
54
|
+
const startup = await client.startSessionGovernance({
|
|
55
|
+
objective: 'Persist governed assistant turns for the charter project.',
|
|
56
|
+
allowed_mutation_surfaces: ['src/', 'scripts/'],
|
|
57
|
+
});
|
|
58
|
+
|
|
59
|
+
const persistedTurn = await client.persistAssistantTurn({
|
|
60
|
+
project_id: charter.project_id,
|
|
61
|
+
session_key: startup.session_key,
|
|
62
|
+
user_request: 'Proceed with the next roadmap slice.',
|
|
63
|
+
assistant_summary: 'Completed the implementation step and persisted the governed turn.',
|
|
64
|
+
changed_files: ['src/web/app.py'],
|
|
65
|
+
});
|
|
66
|
+
```
|
|
67
|
+
|
|
68
|
+
## Integration Notes
|
|
69
|
+
|
|
70
|
+
### Auth Modes
|
|
71
|
+
|
|
72
|
+
The package supports two authentication patterns:
|
|
73
|
+
|
|
74
|
+
1. Bearer-token auth for operator and governed routes.
|
|
75
|
+
2. Compatibility API-key auth for migration-oriented external routes and deployments that still allow shared-key access.
|
|
76
|
+
|
|
77
|
+
Header behavior is:
|
|
78
|
+
|
|
79
|
+
- If `accessToken` or `tokenProvider` is configured, the client sends `Authorization: Bearer <token>`.
|
|
80
|
+
- If bearer auth is not configured, the client sends `X-API-Key` when `apiKey` is provided.
|
|
81
|
+
- If bearer auth is not configured and `clientId` is provided, the client also sends `X-Client-Id`.
|
|
82
|
+
- The client always sends `X-Actor-Id` for audit trails unless overridden.
|
|
83
|
+
|
|
84
|
+
That means this package wraps both operator/governed routes and selected external `/api/v1/*` routes. Pick the auth mode that matches the route family exposed by your deployment.
|
|
85
|
+
|
|
86
|
+
### Route Families
|
|
87
|
+
|
|
88
|
+
- Operator and governed routes are methods such as `createProjectCharter`, `getProjectBundle`, `importImplementationPacket`, and the workflow/portfolio/governance APIs.
|
|
89
|
+
- External routes are the methods prefixed with `getExternal*`, `listExternal*`, `downloadExternal*`, plus `startSessionGovernance`, `persistAssistantTurn`, `createExternalAudioRender`, and `getLatestMemoryProjection`.
|
|
90
|
+
|
|
91
|
+
If your deployment enforces bearer auth on operator routes, API-key-only construction will not work for those methods.
|
|
92
|
+
|
|
93
|
+
### Text and Binary Downloads
|
|
94
|
+
|
|
95
|
+
Some methods do not return plain JSON:
|
|
96
|
+
|
|
97
|
+
- Markdown download helpers return `{ text, contentType, fileName }`.
|
|
98
|
+
- Binary download helpers return `{ arrayBuffer, contentType, fileName }`.
|
|
99
|
+
|
|
100
|
+
Examples:
|
|
101
|
+
|
|
102
|
+
```js
|
|
103
|
+
import fs from 'node:fs';
|
|
104
|
+
|
|
105
|
+
const markdown = await client.downloadProjectCharterReportMarkdown(projectId);
|
|
106
|
+
console.log(markdown.fileName, markdown.contentType);
|
|
107
|
+
console.log(markdown.text);
|
|
108
|
+
|
|
109
|
+
const audio = await client.downloadExternalAudioRender(audioRenderRunId);
|
|
110
|
+
await fs.promises.writeFile('render.mp3', Buffer.from(audio.arrayBuffer));
|
|
41
111
|
```
|
|
42
112
|
|
|
43
113
|
### fromEnv
|
|
44
114
|
|
|
45
115
|
```js
|
|
46
116
|
const client = AIEngineClient.fromEnv();
|
|
47
|
-
// Reads: AI_ENGINE_BASE_URL, AI_ENGINE_API_KEY,
|
|
117
|
+
// Reads: AI_ENGINE_BASE_URL, AI_ENGINE_ACCESS_TOKEN, AI_ENGINE_API_KEY,
|
|
118
|
+
// AI_ENGINE_CLIENT_ID, AI_ENGINE_ACTOR_ID
|
|
48
119
|
```
|
|
49
120
|
|
|
50
121
|
---
|
|
@@ -54,11 +125,156 @@ const client = AIEngineClient.fromEnv();
|
|
|
54
125
|
| Option | Type | Description |
|
|
55
126
|
|---|---|---|
|
|
56
127
|
| `baseUrl` | string | **Required.** Base URL of the AI Engine service. |
|
|
57
|
-
| `
|
|
128
|
+
| `accessToken` | string | Static bearer token sent as `Authorization`. |
|
|
129
|
+
| `tokenProvider` | function | Async callback that returns a bearer token string or `{ token }` / `{ accessToken }` per request. |
|
|
130
|
+
| `apiKey` | string | Compatibility API key sent as `X-API-Key` when bearer auth is not configured. |
|
|
131
|
+
| `clientId` | string | Compatibility client id sent as `X-Client-Id` when bearer auth is not configured. |
|
|
58
132
|
| `actorId` | string | Sent as `X-Actor-Id` for audit trails. Default: `sdk:npm-ai-engine-client`. |
|
|
59
133
|
| `fetchImpl` | function | Custom fetch (Node 18+ built-in used by default). |
|
|
60
134
|
| `timeoutMs` | number | Per-request timeout in ms. Default: 30000. |
|
|
61
135
|
|
|
136
|
+
### Token Provider
|
|
137
|
+
|
|
138
|
+
```js
|
|
139
|
+
const client = new AIEngineClient({
|
|
140
|
+
baseUrl: process.env.AI_ENGINE_BASE_URL,
|
|
141
|
+
tokenProvider: async () => {
|
|
142
|
+
const token = await acquireAccessToken();
|
|
143
|
+
return { token };
|
|
144
|
+
},
|
|
145
|
+
});
|
|
146
|
+
```
|
|
147
|
+
|
|
148
|
+
### API Key Compatibility
|
|
149
|
+
|
|
150
|
+
```js
|
|
151
|
+
const client = new AIEngineClient({
|
|
152
|
+
baseUrl: process.env.AI_ENGINE_BASE_URL,
|
|
153
|
+
clientId: process.env.AI_ENGINE_CLIENT_ID,
|
|
154
|
+
apiKey: process.env.AI_ENGINE_API_KEY,
|
|
155
|
+
});
|
|
156
|
+
```
|
|
157
|
+
|
|
158
|
+
When `accessToken` or `tokenProvider` is present, the client prefers bearer auth. API key headers are only sent when bearer auth is not configured.
|
|
159
|
+
|
|
160
|
+
Some deployments accept a shared `X-API-Key` without `X-Client-Id`, while others require both `X-Client-Id` and `X-API-Key` for API-key auth. If your environment uses client-scoped API keys, provide both values.
|
|
161
|
+
|
|
162
|
+
### Project Chartering Contract
|
|
163
|
+
|
|
164
|
+
`createProjectCharter()` sends snake_case fields to `POST /api/projects/charter`. The method accepts:
|
|
165
|
+
|
|
166
|
+
- `projectName`, `objective`
|
|
167
|
+
- `businessContext`, `successCriteria`, `priority`
|
|
168
|
+
- `constraints`, `inScope`, `outOfScope`, `assumptions`
|
|
169
|
+
- `linkedWorkflows`, `linkedWorkflowSlug`
|
|
170
|
+
- `testingStrategy`, `initialContext`
|
|
171
|
+
- `requestedBy`
|
|
172
|
+
- `ensureTaskSurface`, `assignedTo`, `createAcceptanceSubtasks`
|
|
173
|
+
|
|
174
|
+
The response always includes the charter identifiers:
|
|
175
|
+
|
|
176
|
+
- `project_id`
|
|
177
|
+
- `workflow_id`
|
|
178
|
+
- `workflow_run_id`
|
|
179
|
+
- `implementation_packet_id`
|
|
180
|
+
- `implementation_packet_key`
|
|
181
|
+
- `status`
|
|
182
|
+
|
|
183
|
+
If `ensureTaskSurface` is left as `true`, the server also attempts to attach `task_surface` for the active roadmap item. That field is only available when the server can resolve an active implementation item from the project roadmap. If roadmap state is incomplete in the deployment, charter creation can still succeed while `task_surface` is unavailable.
|
|
184
|
+
|
|
185
|
+
Example:
|
|
186
|
+
|
|
187
|
+
```js
|
|
188
|
+
const charter = await client.createProjectCharter({
|
|
189
|
+
projectName: 'Loga Cognitive Interface Cockpit Delivery',
|
|
190
|
+
objective: 'Deliver the operator cockpit and playback experience.',
|
|
191
|
+
businessContext: 'Ground the first playback surface in convert-document-to-audio.',
|
|
192
|
+
linkedWorkflowSlug: 'convert-document-to-audio',
|
|
193
|
+
ensureTaskSurface: true,
|
|
194
|
+
createAcceptanceSubtasks: true,
|
|
195
|
+
});
|
|
196
|
+
|
|
197
|
+
console.log(charter.project_id);
|
|
198
|
+
console.log(charter.implementation_packet_key);
|
|
199
|
+
console.log(charter.task_surface?.active_item?.implementation_item_id ?? null);
|
|
200
|
+
```
|
|
201
|
+
|
|
202
|
+
### Implementation Packet Import Contract
|
|
203
|
+
|
|
204
|
+
`importImplementationPacket(body)` expects the engine's `implementation_plan_packet` schema, not an arbitrary roadmap-shaped JSON document.
|
|
205
|
+
|
|
206
|
+
Minimum required top-level fields:
|
|
207
|
+
|
|
208
|
+
- `packetType`
|
|
209
|
+
- `packetVersion`
|
|
210
|
+
- `packetId`
|
|
211
|
+
- `title`
|
|
212
|
+
- `phases`
|
|
213
|
+
- `governance.requiredGates`
|
|
214
|
+
|
|
215
|
+
Minimum required fields for each item:
|
|
216
|
+
|
|
217
|
+
- `itemKey`
|
|
218
|
+
- `type`
|
|
219
|
+
- `description`
|
|
220
|
+
- `acceptanceChecks`
|
|
221
|
+
|
|
222
|
+
Allowed packet statuses include `draft`, `approved`, `blocked`, `completed`, and other governed packet lifecycle states. Custom statuses such as `draft-local-artifact` are not valid for import.
|
|
223
|
+
|
|
224
|
+
Minimal example:
|
|
225
|
+
|
|
226
|
+
```js
|
|
227
|
+
await client.importImplementationPacket({
|
|
228
|
+
packetType: 'implementation_plan_packet',
|
|
229
|
+
packetVersion: '1.0',
|
|
230
|
+
packetId: 'impl-loga-cockpit-v1',
|
|
231
|
+
title: 'Loga Cognitive Interface Cockpit Delivery',
|
|
232
|
+
status: 'draft',
|
|
233
|
+
governance: {
|
|
234
|
+
requiredGates: ['intent', 'structure', 'promotion'],
|
|
235
|
+
},
|
|
236
|
+
phases: [
|
|
237
|
+
{
|
|
238
|
+
phaseKey: 'phase_1_foundation',
|
|
239
|
+
title: 'Foundation',
|
|
240
|
+
items: [
|
|
241
|
+
{
|
|
242
|
+
itemKey: 'define-cockpit-surface',
|
|
243
|
+
type: 'ui_slice',
|
|
244
|
+
description: 'Define the first cockpit delivery slice.',
|
|
245
|
+
acceptanceChecks: [
|
|
246
|
+
{ text: 'The initial slice is explicitly bounded.' },
|
|
247
|
+
],
|
|
248
|
+
},
|
|
249
|
+
],
|
|
250
|
+
},
|
|
251
|
+
],
|
|
252
|
+
});
|
|
253
|
+
```
|
|
254
|
+
|
|
255
|
+
The package does not auto-convert nested human planning structures like `tasks` and `subtasks` into durable implementation-item tasks. To create persisted task records after import, use `ensureProjectRoadmapTaskSurface()` or `createImplementationTask()`.
|
|
256
|
+
|
|
257
|
+
A copyable example payload is packaged at `examples/implementation-plan-packet.minimal.json`.
|
|
258
|
+
|
|
259
|
+
### External Audio Render Example
|
|
260
|
+
|
|
261
|
+
```js
|
|
262
|
+
const render = await client.createExternalAudioRender({
|
|
263
|
+
text: '# Weekly Update\n\nThe platform migration is on track.',
|
|
264
|
+
voice: 'alloy',
|
|
265
|
+
});
|
|
266
|
+
|
|
267
|
+
const status = await client.getExternalAudioRender(render.audio_render_run_id);
|
|
268
|
+
const audio = await client.downloadExternalAudioRender(render.audio_render_run_id);
|
|
269
|
+
```
|
|
270
|
+
|
|
271
|
+
### External Workflow Artifact Example
|
|
272
|
+
|
|
273
|
+
```js
|
|
274
|
+
const manifest = await client.listExternalWorkflowRunArtifacts('run-123');
|
|
275
|
+
const packet = await client.downloadExternalWorkflowRunArtifact('run-123', 'package_payload');
|
|
276
|
+
```
|
|
277
|
+
|
|
62
278
|
---
|
|
63
279
|
|
|
64
280
|
## Methods
|
|
@@ -67,6 +283,18 @@ const client = AIEngineClient.fromEnv();
|
|
|
67
283
|
| Method | Description |
|
|
68
284
|
|---|---|
|
|
69
285
|
| `ping()` | Health check + current workflow name/status. |
|
|
286
|
+
| `startSessionGovernance(body)` | Start a governed external session and return the `session_key` required for mutation-capable assistant turns. |
|
|
287
|
+
| `persistAssistantTurn(body)` | Persist an assistant turn to durable SQL through the API and return refreshed status projections. |
|
|
288
|
+
| `createExternalAudioRender({ text, voice, model, speed, file })` | Create a client-scoped external audio render using inline text or multipart upload. |
|
|
289
|
+
| `getExternalAudioRender(audioRenderRunId)` | Read external audio render status for the authenticated client. |
|
|
290
|
+
| `downloadExternalAudioRender(audioRenderRunId)` | Download the rendered MP3 artifact for the authenticated client. |
|
|
291
|
+
| `listExternalWorkflowRunArtifacts(workflowRunId)` | List signed external artifact descriptors for an authenticated external workflow run. |
|
|
292
|
+
| `downloadExternalWorkflowRunArtifact(workflowRunId, artifactType)` | Download one authenticated external workflow-run artifact by type. |
|
|
293
|
+
| `getExternalProjectStatus(projectId)` | API-key-compatible project status read through the external `/api/v1` boundary. |
|
|
294
|
+
| `getExternalProjectRoadmapSummary(projectId)` | API-key-compatible roadmap summary read through the external `/api/v1` boundary. |
|
|
295
|
+
| `getExternalProjectRoadmapActiveItem(projectId)` | API-key-compatible active roadmap item read through the external `/api/v1` boundary. |
|
|
296
|
+
| `listExternalProjectOpenTasks(projectId)` | API-key-compatible open task read through the external `/api/v1` boundary. |
|
|
297
|
+
| `getExternalProjectStatusBundle(projectId)` | One-call API-key-compatible bundle for project status, roadmap summary, active item, and open tasks. |
|
|
70
298
|
|
|
71
299
|
### Operator Status
|
|
72
300
|
| Method | Description |
|
|
@@ -75,9 +303,24 @@ const client = AIEngineClient.fromEnv();
|
|
|
75
303
|
| `currentArchitectureIntegrityStatus()` | Architecture integrity scan results. |
|
|
76
304
|
| `currentSecurityGovernanceStatus({ environment, topN })` | Security governance findings. |
|
|
77
305
|
| `currentCodebaseShapeStatus()` | Codebase shape analysis. |
|
|
78
|
-
| `getLatestMemoryProjection()` | Current SQL memory projection (startup hydration source). |
|
|
306
|
+
| `getLatestMemoryProjection()` | Current SQL memory projection (startup hydration source) through the general external `/api/v1/latest-memory-projection` route. |
|
|
307
|
+
| `currentProjectStatus({ projectId })` | Current project status projection from SQL memory. |
|
|
308
|
+
| `createDatabaseBackup({ databaseName, outputName, noWait })` | Start a database backup using server-owned Azure backup defaults. |
|
|
309
|
+
| `listDatabaseBackups({ prefix, limit })` | List recent backups from the server-owned backup storage location. |
|
|
310
|
+
| `getDatabaseBackup({ backupId })` | Get one backup artifact by backup id. |
|
|
311
|
+
| `listDatabaseBackupOperations({ databaseName, operationFilter, limit })` | List backup/export operations using server-owned Azure defaults. |
|
|
79
312
|
| `getDashboard()` | Operator dashboard payload. |
|
|
80
313
|
|
|
314
|
+
Preferred backup contract: use `createDatabaseBackup`, `listDatabaseBackups`, `getDatabaseBackup`, and `listDatabaseBackupOperations`. Those routes let the service own Azure storage, SQL server, resource group, subscription, and credential resolution.
|
|
315
|
+
|
|
316
|
+
Low-level compatibility methods:
|
|
317
|
+
|
|
318
|
+
| Method | Description |
|
|
319
|
+
|---|---|
|
|
320
|
+
| `runAzureSqlBacpacBackup({ databaseName, storageAccount, ... })` | Start an Azure SQL BACPAC export through the legacy infra-shaped operator API. |
|
|
321
|
+
| `listAzureSqlBacpacBackups({ storageAccount, container, ... })` | List BACPAC exports through the legacy infra-shaped operator API. |
|
|
322
|
+
| `listAzureSqlBacpacBackupOperations({ databaseName, resourceGroup, serverName, ... })` | List Azure SQL operations through the legacy infra-shaped operator API. |
|
|
323
|
+
|
|
81
324
|
### Retrieval Wrapper
|
|
82
325
|
| Method | Description |
|
|
83
326
|
|---|---|
|
|
@@ -95,10 +338,21 @@ const client = AIEngineClient.fromEnv();
|
|
|
95
338
|
| `getProject(projectId)` | Get project detail. |
|
|
96
339
|
| `listCodeFiles({ repositoryId, projectId, language, pathPrefix, page, pageSize })` | Page through inventoried code files. |
|
|
97
340
|
| `getCodeFile(fileId)` | Get file detail. |
|
|
341
|
+
| `getCodeFileContentWindow(fileId, { startLine, endLine })` | Read a bounded file content window from inventory-backed content. |
|
|
98
342
|
| `listCodeSymbolsByFile(fileId, { limit })` | List symbols discovered in a file. |
|
|
99
343
|
| `getCodeSymbol(symbolId, { includeCode, maxLines })` | Get symbol detail. |
|
|
100
344
|
| `searchSymbols({ query, projectScope, maxResults })` | Search inventoried symbols. |
|
|
101
345
|
| `getSymbolRelationships(symbolId, { relationshipType, depth })` | Read symbol relationships from the inventory graph. |
|
|
346
|
+
| `listCodeRelationships({ repositoryId, projectId, relationshipType, limit })` | Read repository/project scoped graph relationships. |
|
|
347
|
+
| `listActionObservations({ repositoryId, projectPath, filePath, symbolId, actionKind, limit })` | Read latest action-grammar observations for a repo scope. |
|
|
348
|
+
| `listCodebaseShapeFindings({ repositoryId, projectPath, filePath, severity, status, limit })` | List latest codebase-shape findings for the selected repo scope. |
|
|
349
|
+
| `listObjectFlowObservations({ repositoryId, projectPath, filePath, objectKind, boundaryKind, limit })` | List latest object-flow observations for the selected repo scope. |
|
|
350
|
+
| `getChangeAnalysis({ fileId, symbolId, limit })` | Aggregate relationships, findings, and structural observations for one file or symbol. |
|
|
351
|
+
| `listRefactorCandidates({ repositoryRoot, limit })` | List governed refactor candidates derived from SQL-backed findings. |
|
|
352
|
+
| `analyzeRefactorCandidate({ filePath, requestedBy, packetId, refactorIntent })` | Build a replayable refactor packet preview for one candidate file. |
|
|
353
|
+
| `getRepoRetrievalPacket(retrievalPacketId)` | Read a retrieval packet through the repo boundary. |
|
|
354
|
+
| `getRepoRetrievalPacketFragments(retrievalPacketId)` | Read the packet fragments for a retrieval packet. |
|
|
355
|
+
| `evaluateProposalScope({ filePath, projectId, changeType, requestedBy, refactorIntent })` | Evaluate governance, scope, and open-task posture for a proposed refactor slice. |
|
|
102
356
|
|
|
103
357
|
### Retrieval Management
|
|
104
358
|
| Method | Description |
|
|
@@ -143,6 +397,7 @@ const client = AIEngineClient.fromEnv();
|
|
|
143
397
|
| `getWorkflowRun(workflowRunId)` | Get run detail. |
|
|
144
398
|
| `listWorkflowArtifacts(workflowRunId)` | List run artifacts. |
|
|
145
399
|
| `getWorkflowRunSubstrate(workflowRunId)` | Get run substrate (sessions, turns). |
|
|
400
|
+
| `getWorkflowPlayback(workflowRunId)` | Get the playback-lite workflow timeline view derived from step runs and artifacts. |
|
|
146
401
|
| `resumeWorkflowRun(workflowRunId, body)` | Resume a paused run. |
|
|
147
402
|
| `listRecentInspectorRuns({ limit })` | List recent runs via inspector. |
|
|
148
403
|
| `inspectWorkflowRun(workflowRunId)` | Full inspector view of a run. |
|
|
@@ -161,6 +416,11 @@ const client = AIEngineClient.fromEnv();
|
|
|
161
416
|
| `createProjectCharter({ projectName, objective, businessContext, successCriteria, priority, constraints, inScope, outOfScope, assumptions, linkedWorkflows, testingStrategy, initialContext, requestedBy })` | Charter a project (writes to durable SQL memory). |
|
|
162
417
|
| `listProjects({ limit, includeInactive, processStatus, charterStatus })` | List projects. |
|
|
163
418
|
| `getProject(projectId)` | Get project detail. |
|
|
419
|
+
| `getProjectCharterReport(projectId)` | Get the SQL-backed charter report payload for a project. |
|
|
420
|
+
| `createProjectMarkdownDownload(projectId, { reportType, includeMarkdown })` | Create a markdown download descriptor for `charter` or `implementation_roadmap`. |
|
|
421
|
+
| `downloadProjectMarkdownReport(projectId, reportType)` | Download a rendered markdown report as text plus filename metadata. |
|
|
422
|
+
| `downloadProjectCharterReportMarkdown(projectId)` | Download the charter markdown report. |
|
|
423
|
+
| `getProjectBundle(projectId)` | Get current status, charter report, and roadmap report in one payload. |
|
|
164
424
|
|
|
165
425
|
### Roadmaps
|
|
166
426
|
| Method | Description |
|
|
@@ -169,6 +429,9 @@ const client = AIEngineClient.fromEnv();
|
|
|
169
429
|
| `getProjectRoadmap(projectId)` | Get full roadmap for a project. |
|
|
170
430
|
| `getProjectRoadmapSummary(projectId)` | Roadmap summary. |
|
|
171
431
|
| `getProjectRoadmapActiveItem(projectId)` | Current active roadmap item. |
|
|
432
|
+
| `getProjectImplementationRoadmapReport(projectId)` | Get the SQL-backed implementation roadmap report payload. |
|
|
433
|
+
| `downloadProjectImplementationRoadmapReportMarkdown(projectId)` | Download the implementation roadmap markdown report. |
|
|
434
|
+
| `ensureProjectRoadmapTaskSurface(projectId, { requestedBy, assignedTo, createAcceptanceSubtasks })` | Materialize the active roadmap item's parent task and acceptance subtasks. |
|
|
172
435
|
| `listProjectOpenTasks(projectId)` | Open tasks for a project. |
|
|
173
436
|
| `getProjectPerformanceMetrics(projectId, { workflowId, workflowRunId, sinceUtc })` | Performance metrics. |
|
|
174
437
|
|
|
@@ -176,6 +439,8 @@ const client = AIEngineClient.fromEnv();
|
|
|
176
439
|
| Method | Description |
|
|
177
440
|
|---|---|
|
|
178
441
|
| `createImplementationTask(implementationItemId, { title, implementationPacketId, ... })` | Create a task on a roadmap item. |
|
|
442
|
+
|
|
443
|
+
`createProjectCharter()` now also accepts `linkedWorkflowSlug`, `ensureTaskSurface`, `assignedTo`, and `createAcceptanceSubtasks` so new API-chartered projects can immediately surface an attachable implementation item and durable task tree.
|
|
179
444
|
| `listImplementationTasks(implementationItemId)` | List tasks. |
|
|
180
445
|
| `listImplementationSubtasks(taskId)` | List subtasks. |
|
|
181
446
|
| `updateImplementationTask(taskId, updates)` | Update a task. |
|
|
@@ -339,4 +604,4 @@ const client = AIEngineClient.fromEnv();
|
|
|
339
604
|
|
|
340
605
|
The package talks to the AI Engine web service. All routes listed above are registered unconditionally in the Flask app and served by the deployed Azure Container App.
|
|
341
606
|
|
|
342
|
-
Operator routes
|
|
607
|
+
Operator and governed routes generally expect bearer-token authorization. The package also wraps selected external `/api/v1/*` routes that support migration-oriented auth modes such as bearer or API-key compatibility, depending on deployment configuration.
|
|
@@ -0,0 +1,76 @@
|
|
|
1
|
+
{
|
|
2
|
+
"packetType": "implementation_plan_packet",
|
|
3
|
+
"packetVersion": "1.0",
|
|
4
|
+
"packetId": "impl-example-governed-slice-v1",
|
|
5
|
+
"title": "Example Governed Slice Implementation Packet",
|
|
6
|
+
"status": "draft",
|
|
7
|
+
"createdBy": {
|
|
8
|
+
"kind": "operator",
|
|
9
|
+
"name": "sdk-example"
|
|
10
|
+
},
|
|
11
|
+
"sourceModel": {
|
|
12
|
+
"producer": "ai-engine-client-readme",
|
|
13
|
+
"purpose": "minimal_import_example"
|
|
14
|
+
},
|
|
15
|
+
"system": {
|
|
16
|
+
"slug": "example-governed-slice",
|
|
17
|
+
"domain": "project-chartering"
|
|
18
|
+
},
|
|
19
|
+
"scope": {
|
|
20
|
+
"productArea": "example-governed-slice",
|
|
21
|
+
"workflowIntent": "Demonstrate the minimum durable implementation packet contract.",
|
|
22
|
+
"targetSurface": "governed-project-execution",
|
|
23
|
+
"inScope": [
|
|
24
|
+
"first delivery slice"
|
|
25
|
+
],
|
|
26
|
+
"outOfScope": [
|
|
27
|
+
"full product rollout"
|
|
28
|
+
]
|
|
29
|
+
},
|
|
30
|
+
"northStar": {
|
|
31
|
+
"summary": "Define a small but valid governed implementation packet."
|
|
32
|
+
},
|
|
33
|
+
"governance": {
|
|
34
|
+
"requiredGates": [
|
|
35
|
+
"intent",
|
|
36
|
+
"structure",
|
|
37
|
+
"promotion"
|
|
38
|
+
],
|
|
39
|
+
"reviewRequired": true,
|
|
40
|
+
"evidenceRequiredForCompletion": true
|
|
41
|
+
},
|
|
42
|
+
"completionPolicy": {
|
|
43
|
+
"requiresEvidence": true,
|
|
44
|
+
"requiresReview": true
|
|
45
|
+
},
|
|
46
|
+
"phases": [
|
|
47
|
+
{
|
|
48
|
+
"phaseKey": "phase_1_foundation",
|
|
49
|
+
"title": "Foundation",
|
|
50
|
+
"items": [
|
|
51
|
+
{
|
|
52
|
+
"itemKey": "define-first-slice",
|
|
53
|
+
"type": "delivery_slice",
|
|
54
|
+
"priority": "high",
|
|
55
|
+
"title": "Define first governed slice",
|
|
56
|
+
"description": "Define the first durable delivery slice and bind its acceptance criteria.",
|
|
57
|
+
"status": "not_started",
|
|
58
|
+
"dependsOn": [],
|
|
59
|
+
"artifactsExpected": [
|
|
60
|
+
"delivery-slice-definition.md"
|
|
61
|
+
],
|
|
62
|
+
"acceptanceChecks": [
|
|
63
|
+
{
|
|
64
|
+
"text": "The slice is explicitly bounded.",
|
|
65
|
+
"status": "not_run"
|
|
66
|
+
},
|
|
67
|
+
{
|
|
68
|
+
"text": "The slice can be attached to durable task surfaces.",
|
|
69
|
+
"status": "not_run"
|
|
70
|
+
}
|
|
71
|
+
]
|
|
72
|
+
}
|
|
73
|
+
]
|
|
74
|
+
}
|
|
75
|
+
]
|
|
76
|
+
}
|
package/package.json
CHANGED
|
@@ -1,28 +1,32 @@
|
|
|
1
|
-
{
|
|
2
|
-
"name": "@bpmsoftwaresolutions/ai-engine-client",
|
|
3
|
-
"version": "1.0.0-beta.
|
|
4
|
-
"description": "Thin npm client for the AI Engine operator and retrieval APIs",
|
|
5
|
-
"type": "module",
|
|
6
|
-
"main": "./src/index.js",
|
|
7
|
-
"
|
|
8
|
-
"
|
|
9
|
-
},
|
|
10
|
-
"
|
|
11
|
-
"src"
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
"
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
"
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
"
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
"
|
|
27
|
-
|
|
28
|
-
|
|
1
|
+
{
|
|
2
|
+
"name": "@bpmsoftwaresolutions/ai-engine-client",
|
|
3
|
+
"version": "1.0.0-beta.11",
|
|
4
|
+
"description": "Thin npm client for the AI Engine operator and retrieval APIs",
|
|
5
|
+
"type": "module",
|
|
6
|
+
"main": "./src/index.js",
|
|
7
|
+
"scripts": {
|
|
8
|
+
"test": "node --test"
|
|
9
|
+
},
|
|
10
|
+
"exports": {
|
|
11
|
+
".": "./src/index.js"
|
|
12
|
+
},
|
|
13
|
+
"files": [
|
|
14
|
+
"src",
|
|
15
|
+
"README.md",
|
|
16
|
+
"examples"
|
|
17
|
+
],
|
|
18
|
+
"engines": {
|
|
19
|
+
"node": ">=18"
|
|
20
|
+
},
|
|
21
|
+
"keywords": [
|
|
22
|
+
"ai-engine",
|
|
23
|
+
"sdk",
|
|
24
|
+
"client",
|
|
25
|
+
"retrieval",
|
|
26
|
+
"workflow"
|
|
27
|
+
],
|
|
28
|
+
"license": "UNLICENSED",
|
|
29
|
+
"publishConfig": {
|
|
30
|
+
"access": "public"
|
|
31
|
+
}
|
|
32
|
+
}
|
package/src/index.js
CHANGED
|
@@ -20,11 +20,42 @@ async function readJson(response) {
|
|
|
20
20
|
return { message: text };
|
|
21
21
|
}
|
|
22
22
|
|
|
23
|
+
function parseContentDispositionFilename(headerValue) {
|
|
24
|
+
const value = String(headerValue || '');
|
|
25
|
+
const quotedMatch = value.match(/filename="([^"]+)"/i);
|
|
26
|
+
if (quotedMatch) return quotedMatch[1];
|
|
27
|
+
const plainMatch = value.match(/filename=([^;]+)/i);
|
|
28
|
+
return plainMatch ? plainMatch[1].trim() : null;
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
function isFormDataBody(value) {
|
|
32
|
+
return typeof FormData !== 'undefined' && value instanceof FormData;
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
function isBinaryBody(value) {
|
|
36
|
+
return (
|
|
37
|
+
value instanceof ArrayBuffer
|
|
38
|
+
|| ArrayBuffer.isView(value)
|
|
39
|
+
|| (typeof Blob !== 'undefined' && value instanceof Blob)
|
|
40
|
+
|| value instanceof URLSearchParams
|
|
41
|
+
|| typeof value === 'string'
|
|
42
|
+
);
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
function isJsonBody(value) {
|
|
46
|
+
if (value === undefined || value === null) return false;
|
|
47
|
+
if (isFormDataBody(value) || isBinaryBody(value)) return false;
|
|
48
|
+
return typeof value === 'object';
|
|
49
|
+
}
|
|
50
|
+
|
|
23
51
|
export class AIEngineClient {
|
|
24
|
-
constructor({ baseUrl, apiKey, actorId, fetchImpl, timeoutMs } = {}) {
|
|
52
|
+
constructor({ baseUrl, accessToken, tokenProvider, apiKey, clientId, actorId, fetchImpl, timeoutMs } = {}) {
|
|
25
53
|
if (!baseUrl) throw new Error('baseUrl is required.');
|
|
26
54
|
this.baseUrl = trimTrailingSlash(baseUrl);
|
|
55
|
+
this.accessToken = accessToken || null;
|
|
56
|
+
this.tokenProvider = tokenProvider || null;
|
|
27
57
|
this.apiKey = apiKey || null;
|
|
58
|
+
this.clientId = clientId || null;
|
|
28
59
|
this.actorId = actorId || 'sdk:npm-ai-engine-client';
|
|
29
60
|
this.fetchImpl = fetchImpl || globalThis.fetch;
|
|
30
61
|
this.timeoutMs = timeoutMs || DEFAULT_TIMEOUT_MS;
|
|
@@ -36,7 +67,10 @@ export class AIEngineClient {
|
|
|
36
67
|
static fromEnv(options = {}) {
|
|
37
68
|
return new AIEngineClient({
|
|
38
69
|
baseUrl: options.baseUrl || process.env.AI_ENGINE_BASE_URL || process.env.AI_ENGINE_API_BASE_URL,
|
|
70
|
+
accessToken: options.accessToken || process.env.AI_ENGINE_ACCESS_TOKEN || null,
|
|
71
|
+
tokenProvider: options.tokenProvider || null,
|
|
39
72
|
apiKey: options.apiKey || process.env.AI_ENGINE_API_KEY || null,
|
|
73
|
+
clientId: options.clientId || process.env.AI_ENGINE_CLIENT_ID || null,
|
|
40
74
|
actorId: options.actorId || process.env.AI_ENGINE_ACTOR_ID || 'sdk:npm-ai-engine-client',
|
|
41
75
|
});
|
|
42
76
|
}
|
|
@@ -77,13 +111,189 @@ export class AIEngineClient {
|
|
|
77
111
|
}
|
|
78
112
|
|
|
79
113
|
async getLatestMemoryProjection() {
|
|
80
|
-
return this._request('/api/
|
|
114
|
+
return this._request('/api/v1/latest-memory-projection');
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
async currentProjectStatus({ projectId } = {}) {
|
|
118
|
+
return this._request('/api/operator/current-project-status', {
|
|
119
|
+
query: { project_id: projectId },
|
|
120
|
+
});
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
async createDatabaseBackup({ databaseName, outputName, noWait } = {}) {
|
|
124
|
+
return this._request('/api/operator/database/backups', {
|
|
125
|
+
method: 'POST',
|
|
126
|
+
body: {
|
|
127
|
+
database_name: databaseName,
|
|
128
|
+
output_name: outputName,
|
|
129
|
+
no_wait: noWait,
|
|
130
|
+
},
|
|
131
|
+
});
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
async listDatabaseBackups({ prefix, limit } = {}) {
|
|
135
|
+
return this._request('/api/operator/database/backups', {
|
|
136
|
+
query: {
|
|
137
|
+
prefix,
|
|
138
|
+
limit,
|
|
139
|
+
},
|
|
140
|
+
});
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
async getDatabaseBackup({ backupId } = {}) {
|
|
144
|
+
return this._request(`/api/operator/database/backups/${encodeURIComponent(backupId)}`);
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
async listDatabaseBackupOperations({ databaseName, operationFilter, limit } = {}) {
|
|
148
|
+
return this._request('/api/operator/database/backups/operations', {
|
|
149
|
+
query: {
|
|
150
|
+
database_name: databaseName,
|
|
151
|
+
operation_filter: operationFilter,
|
|
152
|
+
limit,
|
|
153
|
+
},
|
|
154
|
+
});
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
async runAzureSqlBacpacBackup({
|
|
158
|
+
databaseName,
|
|
159
|
+
storageAccount,
|
|
160
|
+
container,
|
|
161
|
+
resourceGroup,
|
|
162
|
+
serverName,
|
|
163
|
+
outputName,
|
|
164
|
+
adminUser,
|
|
165
|
+
adminPassword,
|
|
166
|
+
subscription,
|
|
167
|
+
storageKey,
|
|
168
|
+
skipContainerCreate,
|
|
169
|
+
noWait,
|
|
170
|
+
} = {}) {
|
|
171
|
+
return this._request('/api/operator/database/backups/azure-sql-bacpac', {
|
|
172
|
+
method: 'POST',
|
|
173
|
+
body: {
|
|
174
|
+
database_name: databaseName,
|
|
175
|
+
storage_account: storageAccount,
|
|
176
|
+
container,
|
|
177
|
+
resource_group: resourceGroup,
|
|
178
|
+
server_name: serverName,
|
|
179
|
+
output_name: outputName,
|
|
180
|
+
admin_user: adminUser,
|
|
181
|
+
admin_password: adminPassword,
|
|
182
|
+
subscription,
|
|
183
|
+
storage_key: storageKey,
|
|
184
|
+
skip_container_create: skipContainerCreate,
|
|
185
|
+
no_wait: noWait,
|
|
186
|
+
},
|
|
187
|
+
});
|
|
188
|
+
}
|
|
189
|
+
|
|
190
|
+
async listAzureSqlBacpacBackups({
|
|
191
|
+
storageAccount,
|
|
192
|
+
container,
|
|
193
|
+
resourceGroup,
|
|
194
|
+
serverName,
|
|
195
|
+
subscription,
|
|
196
|
+
storageKey,
|
|
197
|
+
prefix,
|
|
198
|
+
limit,
|
|
199
|
+
} = {}) {
|
|
200
|
+
return this._request('/api/operator/database/backups/azure-sql-bacpac', {
|
|
201
|
+
query: {
|
|
202
|
+
storage_account: storageAccount,
|
|
203
|
+
container,
|
|
204
|
+
resource_group: resourceGroup,
|
|
205
|
+
server_name: serverName,
|
|
206
|
+
subscription,
|
|
207
|
+
storage_key: storageKey,
|
|
208
|
+
prefix,
|
|
209
|
+
limit,
|
|
210
|
+
},
|
|
211
|
+
});
|
|
212
|
+
}
|
|
213
|
+
|
|
214
|
+
async listAzureSqlBacpacBackupOperations({
|
|
215
|
+
databaseName,
|
|
216
|
+
resourceGroup,
|
|
217
|
+
serverName,
|
|
218
|
+
subscription,
|
|
219
|
+
operationFilter,
|
|
220
|
+
limit,
|
|
221
|
+
} = {}) {
|
|
222
|
+
return this._request('/api/operator/database/backups/azure-sql-bacpac/operations', {
|
|
223
|
+
query: {
|
|
224
|
+
database_name: databaseName,
|
|
225
|
+
resource_group: resourceGroup,
|
|
226
|
+
server_name: serverName,
|
|
227
|
+
subscription,
|
|
228
|
+
operation_filter: operationFilter,
|
|
229
|
+
limit,
|
|
230
|
+
},
|
|
231
|
+
});
|
|
81
232
|
}
|
|
82
233
|
|
|
83
234
|
async getDashboard() {
|
|
84
235
|
return this._request('/api/dashboard');
|
|
85
236
|
}
|
|
86
237
|
|
|
238
|
+
async startSessionGovernance(body) {
|
|
239
|
+
return this._request('/api/v1/session-governance/startup', { method: 'POST', body });
|
|
240
|
+
}
|
|
241
|
+
|
|
242
|
+
async persistAssistantTurn(body) {
|
|
243
|
+
return this._request('/api/v1/assistant-turns', { method: 'POST', body });
|
|
244
|
+
}
|
|
245
|
+
|
|
246
|
+
async getExternalProjectStatus(projectId) {
|
|
247
|
+
return this._request(`/api/v1/projects/${projectId}/status`);
|
|
248
|
+
}
|
|
249
|
+
|
|
250
|
+
async getExternalProjectRoadmapSummary(projectId) {
|
|
251
|
+
return this._request(`/api/v1/projects/${projectId}/implementation-roadmap/summary`);
|
|
252
|
+
}
|
|
253
|
+
|
|
254
|
+
async getExternalProjectRoadmapActiveItem(projectId) {
|
|
255
|
+
return this._request(`/api/v1/projects/${projectId}/implementation-roadmap/active-item`);
|
|
256
|
+
}
|
|
257
|
+
|
|
258
|
+
async listExternalProjectOpenTasks(projectId) {
|
|
259
|
+
return this._request(`/api/v1/projects/${projectId}/open-tasks`);
|
|
260
|
+
}
|
|
261
|
+
|
|
262
|
+
async getExternalProjectStatusBundle(projectId) {
|
|
263
|
+
return this._request(`/api/v1/projects/${projectId}/status-bundle`);
|
|
264
|
+
}
|
|
265
|
+
|
|
266
|
+
async createExternalAudioRender({ text, voice, model, speed, file } = {}) {
|
|
267
|
+
if (file) {
|
|
268
|
+
const form = new FormData();
|
|
269
|
+
form.append('file', file);
|
|
270
|
+
if (voice !== undefined) form.append('voice', String(voice));
|
|
271
|
+
if (model !== undefined) form.append('model', String(model));
|
|
272
|
+
if (speed !== undefined) form.append('speed', String(speed));
|
|
273
|
+
return this._request('/api/v1/audio-renders', { method: 'POST', body: form });
|
|
274
|
+
}
|
|
275
|
+
return this._request('/api/v1/audio-renders', {
|
|
276
|
+
method: 'POST',
|
|
277
|
+
body: { text, voice, model, speed },
|
|
278
|
+
});
|
|
279
|
+
}
|
|
280
|
+
|
|
281
|
+
async getExternalAudioRender(audioRenderRunId) {
|
|
282
|
+
return this._request(`/api/v1/audio-renders/${audioRenderRunId}`);
|
|
283
|
+
}
|
|
284
|
+
|
|
285
|
+
async downloadExternalAudioRender(audioRenderRunId) {
|
|
286
|
+
return this._requestBinary(`/api/v1/audio-renders/${audioRenderRunId}/download`);
|
|
287
|
+
}
|
|
288
|
+
|
|
289
|
+
async listExternalWorkflowRunArtifacts(workflowRunId) {
|
|
290
|
+
return this._request(`/api/v1/runs/${workflowRunId}/artifacts`);
|
|
291
|
+
}
|
|
292
|
+
|
|
293
|
+
async downloadExternalWorkflowRunArtifact(workflowRunId, artifactType) {
|
|
294
|
+
return this._requestBinary(`/api/v1/runs/${workflowRunId}/artifacts/${artifactType}/download`);
|
|
295
|
+
}
|
|
296
|
+
|
|
87
297
|
// ─── Retrieval Wrapper ─────────────────────────────────────────────────────
|
|
88
298
|
|
|
89
299
|
async getCommandCard({ commandKey, alias, intentText, requestedBy } = {}) {
|
|
@@ -165,6 +375,12 @@ export class AIEngineClient {
|
|
|
165
375
|
return this._request(`/api/repo/files/${fileId}`);
|
|
166
376
|
}
|
|
167
377
|
|
|
378
|
+
async getCodeFileContentWindow(fileId, { startLine = 1, endLine } = {}) {
|
|
379
|
+
return this._request(`/api/repo/files/${fileId}/content-window`, {
|
|
380
|
+
query: { start_line: startLine, end_line: endLine ?? startLine },
|
|
381
|
+
});
|
|
382
|
+
}
|
|
383
|
+
|
|
168
384
|
async listCodeSymbolsByFile(fileId, { limit = 500 } = {}) {
|
|
169
385
|
return this._request(`/api/repo/files/${fileId}/symbols`, {
|
|
170
386
|
query: { limit },
|
|
@@ -189,6 +405,64 @@ export class AIEngineClient {
|
|
|
189
405
|
});
|
|
190
406
|
}
|
|
191
407
|
|
|
408
|
+
async listCodeRelationships({ repositoryId, projectId, relationshipType, limit = 100 } = {}) {
|
|
409
|
+
return this._request('/api/repo/relationships', {
|
|
410
|
+
query: { repository_id: repositoryId, project_id: projectId, relationship_type: relationshipType, limit },
|
|
411
|
+
});
|
|
412
|
+
}
|
|
413
|
+
|
|
414
|
+
async listActionObservations({ repositoryId, projectPath, filePath, symbolId, actionKind, limit = 100 } = {}) {
|
|
415
|
+
return this._request('/api/repo/action-observations', {
|
|
416
|
+
query: { repository_id: repositoryId, project_path: projectPath, file_path: filePath, symbol_id: symbolId, action_kind: actionKind, limit },
|
|
417
|
+
});
|
|
418
|
+
}
|
|
419
|
+
|
|
420
|
+
async listCodebaseShapeFindings({ repositoryId, projectPath, filePath, severity, status, limit = 100 } = {}) {
|
|
421
|
+
return this._request('/api/repo/shape/findings', {
|
|
422
|
+
query: { repository_id: repositoryId, project_path: projectPath, file_path: filePath, severity, status, limit },
|
|
423
|
+
});
|
|
424
|
+
}
|
|
425
|
+
|
|
426
|
+
async listObjectFlowObservations({ repositoryId, projectPath, filePath, objectKind, boundaryKind, limit = 100 } = {}) {
|
|
427
|
+
return this._request('/api/repo/object-flow-observations', {
|
|
428
|
+
query: { repository_id: repositoryId, project_path: projectPath, file_path: filePath, object_kind: objectKind, boundary_kind: boundaryKind, limit },
|
|
429
|
+
});
|
|
430
|
+
}
|
|
431
|
+
|
|
432
|
+
async getChangeAnalysis({ fileId, symbolId, limit = 25 } = {}) {
|
|
433
|
+
return this._request('/api/repo/change-analysis', {
|
|
434
|
+
query: { file_id: fileId, symbol_id: symbolId, limit },
|
|
435
|
+
});
|
|
436
|
+
}
|
|
437
|
+
|
|
438
|
+
async listRefactorCandidates({ repositoryRoot, limit = 10 } = {}) {
|
|
439
|
+
return this._request('/api/repo/refactor-candidates', {
|
|
440
|
+
query: { repository_root: repositoryRoot, limit },
|
|
441
|
+
});
|
|
442
|
+
}
|
|
443
|
+
|
|
444
|
+
async analyzeRefactorCandidate({ filePath, requestedBy, packetId, refactorIntent } = {}) {
|
|
445
|
+
return this._request('/api/repo/refactor-candidate-analysis', {
|
|
446
|
+
method: 'POST',
|
|
447
|
+
body: { file_path: filePath, requested_by: requestedBy, packet_id: packetId, refactor_intent: refactorIntent },
|
|
448
|
+
});
|
|
449
|
+
}
|
|
450
|
+
|
|
451
|
+
async getRepoRetrievalPacket(retrievalPacketId) {
|
|
452
|
+
return this._request(`/api/repo/retrieval/packets/${retrievalPacketId}`);
|
|
453
|
+
}
|
|
454
|
+
|
|
455
|
+
async getRepoRetrievalPacketFragments(retrievalPacketId) {
|
|
456
|
+
return this._request(`/api/repo/retrieval/packets/${retrievalPacketId}/fragments`);
|
|
457
|
+
}
|
|
458
|
+
|
|
459
|
+
async evaluateProposalScope({ filePath, projectId, changeType = 'refactor', requestedBy, refactorIntent } = {}) {
|
|
460
|
+
return this._request('/api/repo/proposal-scope-evaluation', {
|
|
461
|
+
method: 'POST',
|
|
462
|
+
body: { file_path: filePath, project_id: projectId, change_type: changeType, requested_by: requestedBy, refactor_intent: refactorIntent },
|
|
463
|
+
});
|
|
464
|
+
}
|
|
465
|
+
|
|
192
466
|
// ─── Retrieval Management ──────────────────────────────────────────────────
|
|
193
467
|
|
|
194
468
|
async getRetrievalStatus() {
|
|
@@ -316,6 +590,10 @@ export class AIEngineClient {
|
|
|
316
590
|
return this._request(`/api/workflow-runs/${workflowRunId}/substrate`);
|
|
317
591
|
}
|
|
318
592
|
|
|
593
|
+
async getWorkflowPlayback(workflowRunId) {
|
|
594
|
+
return this._request(`/api/operator/workflow-runs/${workflowRunId}/playback`);
|
|
595
|
+
}
|
|
596
|
+
|
|
319
597
|
async resumeWorkflowRun(workflowRunId, body = {}) {
|
|
320
598
|
return this._request(`/api/workflow-runs/${workflowRunId}/resume`, { method: 'POST', body });
|
|
321
599
|
}
|
|
@@ -361,9 +639,13 @@ export class AIEngineClient {
|
|
|
361
639
|
outOfScope = [],
|
|
362
640
|
assumptions = [],
|
|
363
641
|
linkedWorkflows = [],
|
|
642
|
+
linkedWorkflowSlug,
|
|
364
643
|
testingStrategy = {},
|
|
365
644
|
initialContext = {},
|
|
366
645
|
requestedBy,
|
|
646
|
+
ensureTaskSurface = true,
|
|
647
|
+
assignedTo,
|
|
648
|
+
createAcceptanceSubtasks = true,
|
|
367
649
|
} = {}) {
|
|
368
650
|
return this._request('/api/projects/charter', {
|
|
369
651
|
method: 'POST',
|
|
@@ -378,9 +660,13 @@ export class AIEngineClient {
|
|
|
378
660
|
out_of_scope: outOfScope,
|
|
379
661
|
assumptions,
|
|
380
662
|
linked_workflows: linkedWorkflows,
|
|
663
|
+
linked_workflow_slug: linkedWorkflowSlug,
|
|
381
664
|
testing_strategy: testingStrategy,
|
|
382
665
|
initial_context: initialContext,
|
|
383
666
|
requested_by: requestedBy,
|
|
667
|
+
ensure_task_surface: ensureTaskSurface,
|
|
668
|
+
assigned_to: assignedTo,
|
|
669
|
+
create_acceptance_subtasks: createAcceptanceSubtasks,
|
|
384
670
|
},
|
|
385
671
|
});
|
|
386
672
|
}
|
|
@@ -400,6 +686,29 @@ export class AIEngineClient {
|
|
|
400
686
|
return this._request(`/api/operator/projects/${projectId}`);
|
|
401
687
|
}
|
|
402
688
|
|
|
689
|
+
async getProjectCharterReport(projectId) {
|
|
690
|
+
return this._request(`/api/operator/projects/${projectId}/charter/report`);
|
|
691
|
+
}
|
|
692
|
+
|
|
693
|
+
async createProjectMarkdownDownload(projectId, { reportType, includeMarkdown = false } = {}) {
|
|
694
|
+
return this._request(`/api/operator/projects/${projectId}/markdown-report-downloads`, {
|
|
695
|
+
method: 'POST',
|
|
696
|
+
body: { report_type: reportType, include_markdown: includeMarkdown },
|
|
697
|
+
});
|
|
698
|
+
}
|
|
699
|
+
|
|
700
|
+
async downloadProjectMarkdownReport(projectId, reportType) {
|
|
701
|
+
return this._requestText(`/api/operator/projects/${projectId}/markdown-reports/${reportType}/download`);
|
|
702
|
+
}
|
|
703
|
+
|
|
704
|
+
async downloadProjectCharterReportMarkdown(projectId) {
|
|
705
|
+
return this.downloadProjectMarkdownReport(projectId, 'charter');
|
|
706
|
+
}
|
|
707
|
+
|
|
708
|
+
async getProjectBundle(projectId) {
|
|
709
|
+
return this._request(`/api/operator/projects/${projectId}/bundle`);
|
|
710
|
+
}
|
|
711
|
+
|
|
403
712
|
// ─── Roadmaps ──────────────────────────────────────────────────────────────
|
|
404
713
|
|
|
405
714
|
async listProjectRoadmaps({ includeInactive } = {}) {
|
|
@@ -420,6 +729,29 @@ export class AIEngineClient {
|
|
|
420
729
|
return this._request(`/api/operator/projects/${projectId}/implementation-roadmap/active-item`);
|
|
421
730
|
}
|
|
422
731
|
|
|
732
|
+
async getProjectImplementationRoadmapReport(projectId) {
|
|
733
|
+
return this._request(`/api/operator/projects/${projectId}/implementation-roadmap/report`);
|
|
734
|
+
}
|
|
735
|
+
|
|
736
|
+
async downloadProjectImplementationRoadmapReportMarkdown(projectId) {
|
|
737
|
+
return this.downloadProjectMarkdownReport(projectId, 'implementation_roadmap');
|
|
738
|
+
}
|
|
739
|
+
|
|
740
|
+
async ensureProjectRoadmapTaskSurface(projectId, {
|
|
741
|
+
requestedBy,
|
|
742
|
+
assignedTo,
|
|
743
|
+
createAcceptanceSubtasks = true,
|
|
744
|
+
} = {}) {
|
|
745
|
+
return this._request(`/api/operator/projects/${projectId}/implementation-roadmap/task-surface`, {
|
|
746
|
+
method: 'POST',
|
|
747
|
+
body: {
|
|
748
|
+
requested_by: requestedBy,
|
|
749
|
+
assigned_to: assignedTo,
|
|
750
|
+
create_acceptance_subtasks: createAcceptanceSubtasks,
|
|
751
|
+
},
|
|
752
|
+
});
|
|
753
|
+
}
|
|
754
|
+
|
|
423
755
|
async listProjectOpenTasks(projectId) {
|
|
424
756
|
return this._request(`/api/operator/projects/${projectId}/open-tasks`);
|
|
425
757
|
}
|
|
@@ -976,21 +1308,45 @@ export class AIEngineClient {
|
|
|
976
1308
|
|
|
977
1309
|
// ─── Core HTTP ─────────────────────────────────────────────────────────────
|
|
978
1310
|
|
|
1311
|
+
async _resolveAccessToken() {
|
|
1312
|
+
if (typeof this.tokenProvider === 'function') {
|
|
1313
|
+
const provided = await this.tokenProvider();
|
|
1314
|
+
if (!provided) return null;
|
|
1315
|
+
if (typeof provided === 'string') return provided;
|
|
1316
|
+
if (typeof provided === 'object' && typeof provided.token === 'string') return provided.token;
|
|
1317
|
+
if (typeof provided === 'object' && typeof provided.accessToken === 'string') return provided.accessToken;
|
|
1318
|
+
throw new Error('tokenProvider must return a token string or an object with a token or accessToken field.');
|
|
1319
|
+
}
|
|
1320
|
+
return this.accessToken;
|
|
1321
|
+
}
|
|
1322
|
+
|
|
1323
|
+
async _buildHeaders({ headers, body, accept }) {
|
|
1324
|
+
const token = await this._resolveAccessToken();
|
|
1325
|
+
const resolvedHeaders = {
|
|
1326
|
+
accept: accept || 'application/json',
|
|
1327
|
+
'x-actor-id': this.actorId,
|
|
1328
|
+
...(isJsonBody(body) ? { 'content-type': 'application/json' } : {}),
|
|
1329
|
+
...(token ? { authorization: `Bearer ${token}` } : {}),
|
|
1330
|
+
...(!token && this.clientId ? { 'x-client-id': this.clientId } : {}),
|
|
1331
|
+
...(!token && this.apiKey ? { 'x-api-key': this.apiKey } : {}),
|
|
1332
|
+
...headers,
|
|
1333
|
+
};
|
|
1334
|
+
for (const [key, value] of Object.entries(resolvedHeaders)) {
|
|
1335
|
+
if (value === undefined) delete resolvedHeaders[key];
|
|
1336
|
+
}
|
|
1337
|
+
return resolvedHeaders;
|
|
1338
|
+
}
|
|
1339
|
+
|
|
979
1340
|
async _request(path, { method = 'GET', query, headers, body } = {}) {
|
|
980
1341
|
const url = appendQuery(`${this.baseUrl}${path}`, query);
|
|
981
1342
|
const controller = new AbortController();
|
|
982
1343
|
const timeoutHandle = setTimeout(() => controller.abort(), this.timeoutMs);
|
|
983
1344
|
try {
|
|
1345
|
+
const resolvedHeaders = await this._buildHeaders({ headers, body, accept: 'application/json' });
|
|
984
1346
|
const response = await this.fetchImpl(url, {
|
|
985
1347
|
method,
|
|
986
|
-
headers:
|
|
987
|
-
|
|
988
|
-
'content-type': body ? 'application/json' : undefined,
|
|
989
|
-
'x-actor-id': this.actorId,
|
|
990
|
-
authorization: this.apiKey ? `Bearer ${this.apiKey}` : undefined,
|
|
991
|
-
...headers,
|
|
992
|
-
},
|
|
993
|
-
body: body ? JSON.stringify(body) : undefined,
|
|
1348
|
+
headers: resolvedHeaders,
|
|
1349
|
+
body: isJsonBody(body) ? JSON.stringify(body) : body,
|
|
994
1350
|
signal: controller.signal,
|
|
995
1351
|
});
|
|
996
1352
|
const payload = await readJson(response);
|
|
@@ -1005,6 +1361,81 @@ export class AIEngineClient {
|
|
|
1005
1361
|
clearTimeout(timeoutHandle);
|
|
1006
1362
|
}
|
|
1007
1363
|
}
|
|
1364
|
+
|
|
1365
|
+
async _requestText(path, { method = 'GET', query, headers, body } = {}) {
|
|
1366
|
+
const url = appendQuery(`${this.baseUrl}${path}`, query);
|
|
1367
|
+
const controller = new AbortController();
|
|
1368
|
+
const timeoutHandle = setTimeout(() => controller.abort(), this.timeoutMs);
|
|
1369
|
+
try {
|
|
1370
|
+
const resolvedHeaders = await this._buildHeaders({
|
|
1371
|
+
headers,
|
|
1372
|
+
body,
|
|
1373
|
+
accept: 'text/markdown, text/plain;q=0.9, application/json;q=0.8',
|
|
1374
|
+
});
|
|
1375
|
+
const response = await this.fetchImpl(url, {
|
|
1376
|
+
method,
|
|
1377
|
+
headers: resolvedHeaders,
|
|
1378
|
+
body: isJsonBody(body) ? JSON.stringify(body) : body,
|
|
1379
|
+
signal: controller.signal,
|
|
1380
|
+
});
|
|
1381
|
+
const contentType = response.headers.get('content-type') || '';
|
|
1382
|
+
const contentDisposition = response.headers.get('content-disposition') || '';
|
|
1383
|
+
if (!response.ok) {
|
|
1384
|
+
const payload = contentType.includes('application/json')
|
|
1385
|
+
? await response.json()
|
|
1386
|
+
: { message: await response.text() };
|
|
1387
|
+
const error = new Error(payload?.message || payload?.error || `Request failed with status ${response.status}.`);
|
|
1388
|
+
error.status = response.status;
|
|
1389
|
+
error.payload = payload;
|
|
1390
|
+
throw error;
|
|
1391
|
+
}
|
|
1392
|
+
return {
|
|
1393
|
+
text: await response.text(),
|
|
1394
|
+
contentType,
|
|
1395
|
+
fileName: parseContentDispositionFilename(contentDisposition),
|
|
1396
|
+
};
|
|
1397
|
+
} finally {
|
|
1398
|
+
clearTimeout(timeoutHandle);
|
|
1399
|
+
}
|
|
1400
|
+
}
|
|
1401
|
+
|
|
1402
|
+
async _requestBinary(path, { method = 'GET', query, headers, body } = {}) {
|
|
1403
|
+
const url = appendQuery(`${this.baseUrl}${path}`, query);
|
|
1404
|
+
const controller = new AbortController();
|
|
1405
|
+
const timeoutHandle = setTimeout(() => controller.abort(), this.timeoutMs);
|
|
1406
|
+
try {
|
|
1407
|
+
const resolvedHeaders = await this._buildHeaders({
|
|
1408
|
+
headers,
|
|
1409
|
+
body,
|
|
1410
|
+
accept: 'audio/mpeg, application/octet-stream;q=0.9, application/json;q=0.8',
|
|
1411
|
+
});
|
|
1412
|
+
const response = await this.fetchImpl(url, {
|
|
1413
|
+
method,
|
|
1414
|
+
headers: resolvedHeaders,
|
|
1415
|
+
body: isJsonBody(body) ? JSON.stringify(body) : body,
|
|
1416
|
+
signal: controller.signal,
|
|
1417
|
+
});
|
|
1418
|
+
const contentType = response.headers.get('content-type') || '';
|
|
1419
|
+
const contentDisposition = response.headers.get('content-disposition') || '';
|
|
1420
|
+
if (!response.ok) {
|
|
1421
|
+
const payload = contentType.includes('application/json')
|
|
1422
|
+
? await response.json()
|
|
1423
|
+
: { message: await response.text() };
|
|
1424
|
+
const error = new Error(payload?.message || payload?.error || `Request failed with status ${response.status}.`);
|
|
1425
|
+
error.status = response.status;
|
|
1426
|
+
error.payload = payload;
|
|
1427
|
+
throw error;
|
|
1428
|
+
}
|
|
1429
|
+
const arrayBuffer = await response.arrayBuffer();
|
|
1430
|
+
return {
|
|
1431
|
+
arrayBuffer,
|
|
1432
|
+
contentType,
|
|
1433
|
+
fileName: parseContentDispositionFilename(contentDisposition),
|
|
1434
|
+
};
|
|
1435
|
+
} finally {
|
|
1436
|
+
clearTimeout(timeoutHandle);
|
|
1437
|
+
}
|
|
1438
|
+
}
|
|
1008
1439
|
}
|
|
1009
1440
|
|
|
1010
1441
|
export function createAIEngineClient(options) {
|