aui-agent-builder 0.3.83 → 0.3.85
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/dist/api-client/index.d.ts +0 -17
- package/dist/api-client/index.d.ts.map +1 -1
- package/dist/api-client/index.js +4 -15
- package/dist/api-client/index.js.map +1 -1
- package/dist/commands/import-agent.js +4 -5
- package/dist/commands/import-agent.js.map +1 -1
- package/dist/commands/pull-agent.js +3 -14
- package/dist/commands/pull-agent.js.map +1 -1
- package/dist/commands/push.d.ts +70 -1
- package/dist/commands/push.d.ts.map +1 -1
- package/dist/commands/push.js +603 -432
- package/dist/commands/push.js.map +1 -1
- package/dist/config/index.d.ts +1 -9
- package/dist/config/index.d.ts.map +1 -1
- package/dist/config/index.js.map +1 -1
- package/package.json +1 -1
package/dist/commands/push.js
CHANGED
|
@@ -237,208 +237,15 @@ async function _push(pushSpan, agentCode, options = {}) {
|
|
|
237
237
|
log(_jsx(Box, { paddingX: 1, children: _jsx(StatusLine, { kind: "muted", label: "Dry run \u2014 no changes pushed." }) }));
|
|
238
238
|
return;
|
|
239
239
|
}
|
|
240
|
-
// ─── Push integration upserts before KB ───
|
|
241
|
-
// KB uploads need the integration (and its knowledge base) to exist first.
|
|
242
|
-
// Only create/patch/replace integrations here — not deletes: entities/workflows
|
|
243
|
-
// may still reference an integration until we push entity changes below.
|
|
244
|
-
let integrationUpsertsAlreadyPushed = false;
|
|
245
|
-
/** When set, reused in the main Agent Settings push — avoids resolving the draft twice. */
|
|
246
|
-
let resolvedPushDraftCache = null;
|
|
247
|
-
let cachedPushAgentManagementId;
|
|
248
|
-
if (diff && diff.hasChanges) {
|
|
249
|
-
const intSession = await getValidSession();
|
|
250
|
-
if (intSession) {
|
|
251
|
-
const intAsp = await resolveAgentSettingsParams(config, projectConfig, intSession, projectRoot);
|
|
252
|
-
if (intAsp) {
|
|
253
|
-
const intClient = new AUIClient({
|
|
254
|
-
baseUrl: config.apiUrl,
|
|
255
|
-
authToken: config.authToken,
|
|
256
|
-
accountId: config.accountId,
|
|
257
|
-
organizationId: config.organizationId,
|
|
258
|
-
environment: config.environment,
|
|
259
|
-
});
|
|
260
|
-
const intPushLogDir = path.join(projectRoot, ".aui", "push-logs");
|
|
261
|
-
fs.mkdirSync(intPushLogDir, { recursive: true });
|
|
262
|
-
intClient.setPushLogDir(intPushLogDir);
|
|
263
|
-
if (options.apiKey) {
|
|
264
|
-
saveAgentSettingsApiKey(options.apiKey);
|
|
265
|
-
intClient.setAgentSettingsApiKey(options.apiKey);
|
|
266
|
-
}
|
|
267
|
-
const intSavedKey = loadAgentSettingsApiKey();
|
|
268
|
-
if (intSavedKey && !options.apiKey) {
|
|
269
|
-
intClient.setAgentSettingsApiKey(intSavedKey);
|
|
270
|
-
}
|
|
271
|
-
const intPushTasks = buildPushTasks(diff, fileData, projectRoot, getFileDiff);
|
|
272
|
-
const integrationUpsertTasksEarly = intPushTasks.filter(isIntegrationUpsertTask);
|
|
273
|
-
if (integrationUpsertTasksEarly.length > 0) {
|
|
274
|
-
// Same as main push: integrations must carry agent_version_id on the body.
|
|
275
|
-
// Without this, pre-KB upserts omit version_id while parameters / scope_entities
|
|
276
|
-
// / rules use agentSettingsParams after resolveVersionDraft (and main skips upserts).
|
|
277
|
-
if (projectConfig.version_id || options.versionId) {
|
|
278
|
-
resolvedPushDraftCache = await resolveVersionDraft(config, projectConfig, intSession, options.versionId);
|
|
279
|
-
intAsp.version_id = resolvedPushDraftCache.versionId;
|
|
280
|
-
intAsp.agent_id = resolvedPushDraftCache.agentId;
|
|
281
|
-
cachedPushAgentManagementId = resolvedPushDraftCache.agentId;
|
|
282
|
-
}
|
|
283
|
-
else {
|
|
284
|
-
if (!cachedPushAgentManagementId) {
|
|
285
|
-
cachedPushAgentManagementId = await resolvePushAgentManagementId(config, projectConfig, intSession, projectRoot);
|
|
286
|
-
}
|
|
287
|
-
intAsp.agent_id = cachedPushAgentManagementId;
|
|
288
|
-
}
|
|
289
|
-
log(_jsx(Box, { paddingX: 1, children: _jsxs(Text, { color: colors.info, children: [icons.bullet, " Pushing integrations (before KB upload)..."] }) }));
|
|
290
|
-
for (const task of integrationUpsertTasksEarly) {
|
|
291
|
-
const taskResult = {
|
|
292
|
-
label: task.label,
|
|
293
|
-
status: "success",
|
|
294
|
-
};
|
|
295
|
-
try {
|
|
296
|
-
await executePushTask(intClient, intAsp, task);
|
|
297
|
-
}
|
|
298
|
-
catch (err) {
|
|
299
|
-
const errMsg = err instanceof Error ? err.message : String(err);
|
|
300
|
-
taskResult.status = "failed";
|
|
301
|
-
taskResult.error = errMsg;
|
|
302
|
-
}
|
|
303
|
-
log(_jsx(Box, { paddingX: 2, children: _jsx(PushTaskLine, { result: taskResult }) }));
|
|
304
|
-
}
|
|
305
|
-
integrationUpsertsAlreadyPushed = true;
|
|
306
|
-
}
|
|
307
|
-
}
|
|
308
|
-
}
|
|
309
|
-
}
|
|
310
|
-
// ─── Knowledge Hubs Push (full files) ───
|
|
311
|
-
const { getKnowledgeHubChanges } = await import("../utils/git.js");
|
|
312
|
-
const kbChanges = getKnowledgeHubChanges(projectRoot);
|
|
313
|
-
if (kbChanges.length > 0) {
|
|
314
|
-
const kbConfig = getConfig();
|
|
315
|
-
const kbSession = await getValidSession();
|
|
316
|
-
const kbNetworkId = projectConfig.agent_id || kbSession?.network_id;
|
|
317
|
-
if (kbNetworkId && kbConfig.authToken) {
|
|
318
|
-
const { KBViewClient } = await import("../api-client/kb-view-client.js");
|
|
319
|
-
const { buildScope, readKbFolder } = await import("../services/kb-view.service.js");
|
|
320
|
-
const { loadAgentSettingsApiKey: loadAsKey } = await import("../config/index.js");
|
|
321
|
-
const kbViewClient = new KBViewClient({
|
|
322
|
-
authToken: kbConfig.authToken,
|
|
323
|
-
apiKey: loadAsKey() || undefined,
|
|
324
|
-
organizationId: kbConfig.organizationId || "",
|
|
325
|
-
environment: kbConfig.environment || "staging",
|
|
326
|
-
});
|
|
327
|
-
const kbLogDir = path.join(projectRoot, ".aui", "push-logs");
|
|
328
|
-
fs.mkdirSync(kbLogDir, { recursive: true });
|
|
329
|
-
kbViewClient.setPushLogDir(kbLogDir);
|
|
330
|
-
const scope = buildScope({
|
|
331
|
-
networkId: kbNetworkId,
|
|
332
|
-
organizationId: projectConfig.organization_id || kbConfig.organizationId || "",
|
|
333
|
-
accountId: projectConfig.account_id || kbConfig.accountId || "",
|
|
334
|
-
});
|
|
335
|
-
const userId = kbSession?.user_id || "cli";
|
|
336
|
-
// Collect all changed KB directories (skip root-level files)
|
|
337
|
-
const changedKBDirs = new Set();
|
|
338
|
-
for (const change of kbChanges) {
|
|
339
|
-
if (change.kbDirName) {
|
|
340
|
-
changedKBDirs.add(change.kbDirName);
|
|
341
|
-
}
|
|
342
|
-
}
|
|
343
|
-
// Split into existing (will upload) and deleted (will delete from server)
|
|
344
|
-
const existingKBDirs = [...changedKBDirs].filter((d) => fs.existsSync(path.join(projectRoot, "knowledge-hubs", d)));
|
|
345
|
-
const deletedKBDirs = [...changedKBDirs].filter((d) => !fs.existsSync(path.join(projectRoot, "knowledge-hubs", d)));
|
|
346
|
-
// Delete KBs that were removed locally
|
|
347
|
-
let kbDeleteSucceeded = true;
|
|
348
|
-
if (deletedKBDirs.length > 0) {
|
|
349
|
-
const { getBaselineFileContent } = await import("../utils/git.js");
|
|
350
|
-
const deleteSpinner = startSpinner(`Deleting ${deletedKBDirs.length} knowledge base(s) from server...`);
|
|
351
|
-
try {
|
|
352
|
-
for (const kbDirName of deletedKBDirs) {
|
|
353
|
-
const baselineKb = getBaselineFileContent(projectRoot, `knowledge-hubs/${kbDirName}/kb.json`);
|
|
354
|
-
const kbName = baselineKb?.name || kbDirName;
|
|
355
|
-
const kbId = baselineKb?.knowledge_base_id;
|
|
356
|
-
if (!kbId) {
|
|
357
|
-
log(_jsx(Box, { paddingX: 1, children: _jsx(StatusLine, { kind: "warning", label: `Cannot delete "${kbName}" — no knowledge_base_id stored. Push the KB first, then delete.` }) }));
|
|
358
|
-
continue;
|
|
359
|
-
}
|
|
360
|
-
await kbViewClient.deleteKnowledgeBase(kbId, scope, kbName);
|
|
361
|
-
log(_jsx(Box, { paddingX: 1, children: _jsx(StatusLine, { kind: "success", label: `Deleted: ${kbName}` }) }));
|
|
362
|
-
}
|
|
363
|
-
deleteSpinner.succeed(`${deletedKBDirs.length} knowledge base(s) deleted`);
|
|
364
|
-
}
|
|
365
|
-
catch (error) {
|
|
366
|
-
kbDeleteSucceeded = false;
|
|
367
|
-
deleteSpinner.fail("Knowledge base deletion failed");
|
|
368
|
-
log(_jsx(ErrorDisplay, { error: error }));
|
|
369
|
-
}
|
|
370
|
-
}
|
|
371
|
-
// Upload full files for each changed KB
|
|
372
|
-
let kbUploadSucceeded = false;
|
|
373
|
-
if (existingKBDirs.length > 0) {
|
|
374
|
-
const kbSpinner = startSpinner(`Pushing ${existingKBDirs.length} knowledge base(s)...`);
|
|
375
|
-
try {
|
|
376
|
-
for (const kbDirName of existingKBDirs) {
|
|
377
|
-
const kbDir = path.join(projectRoot, "knowledge-hubs", kbDirName);
|
|
378
|
-
const kbData = readKbFolder(kbDir);
|
|
379
|
-
if (!kbData)
|
|
380
|
-
continue;
|
|
381
|
-
const SUPPORTED_EXTENSIONS = new Set([".pdf", ".md", ".txt", ".json"]);
|
|
382
|
-
const supportedFiles = kbData.binaryFiles.filter((f) => SUPPORTED_EXTENSIONS.has(path.extname(f).toLowerCase()));
|
|
383
|
-
const skippedFiles = kbData.binaryFiles.filter((f) => !SUPPORTED_EXTENSIONS.has(path.extname(f).toLowerCase()));
|
|
384
|
-
for (const skipped of skippedFiles) {
|
|
385
|
-
log(_jsx(Box, { paddingX: 1, children: _jsx(StatusLine, { kind: "warning", label: `Skipped unsupported file: ${path.basename(skipped)} (only .pdf, .md, .txt, .json)` }) }));
|
|
386
|
-
}
|
|
387
|
-
if (supportedFiles.length > 0) {
|
|
388
|
-
const importResult = await kbViewClient.importFiles({
|
|
389
|
-
files: supportedFiles,
|
|
390
|
-
scope,
|
|
391
|
-
created_by: userId,
|
|
392
|
-
knowledge_base_name: kbData.name,
|
|
393
|
-
knowledge_base_description: kbData.description,
|
|
394
|
-
});
|
|
395
|
-
if (importResult.knowledge_base_id) {
|
|
396
|
-
const kbJsonPath = path.join(kbDir, "kb.json");
|
|
397
|
-
try {
|
|
398
|
-
const raw = JSON.parse(fs.readFileSync(kbJsonPath, "utf-8"));
|
|
399
|
-
raw.knowledge_base_id = importResult.knowledge_base_id;
|
|
400
|
-
fs.writeFileSync(kbJsonPath, JSON.stringify(raw, null, 2) + "\n");
|
|
401
|
-
}
|
|
402
|
-
catch { /* kb.json write failed, non-fatal */ }
|
|
403
|
-
}
|
|
404
|
-
}
|
|
405
|
-
}
|
|
406
|
-
kbSpinner.succeed(`Knowledge base(s) pushed`);
|
|
407
|
-
kbUploadSucceeded = true;
|
|
408
|
-
}
|
|
409
|
-
catch (error) {
|
|
410
|
-
kbSpinner.fail("Knowledge base push failed");
|
|
411
|
-
log(_jsx(ErrorDisplay, { error: error }));
|
|
412
|
-
}
|
|
413
|
-
}
|
|
414
|
-
else {
|
|
415
|
-
kbUploadSucceeded = true;
|
|
416
|
-
}
|
|
417
|
-
const kbPushSucceeded = kbUploadSucceeded && kbDeleteSucceeded;
|
|
418
|
-
// Commit KB changes to baseline only if push succeeded
|
|
419
|
-
if (kbPushSucceeded) {
|
|
420
|
-
const kbFilesToAdd = kbChanges
|
|
421
|
-
.filter((c) => c.status !== "deleted")
|
|
422
|
-
.map((c) => c.file);
|
|
423
|
-
const kbFilesToDelete = kbChanges
|
|
424
|
-
.filter((c) => c.status === "deleted")
|
|
425
|
-
.map((c) => c.file);
|
|
426
|
-
if (kbFilesToAdd.length > 0 || kbFilesToDelete.length > 0) {
|
|
427
|
-
const { commitBaselineFiles: commitKBFiles, removeBaselineFiles } = await import("../utils/git.js");
|
|
428
|
-
if (kbFilesToDelete.length > 0) {
|
|
429
|
-
removeBaselineFiles(projectRoot, kbFilesToDelete);
|
|
430
|
-
}
|
|
431
|
-
if (kbFilesToAdd.length > 0) {
|
|
432
|
-
commitKBFiles(projectRoot, kbFilesToAdd, "pushed knowledge hub changes");
|
|
433
|
-
}
|
|
434
|
-
else {
|
|
435
|
-
commitKBFiles(projectRoot, [], "removed knowledge hub files");
|
|
436
|
-
}
|
|
437
|
-
}
|
|
438
|
-
}
|
|
439
|
-
}
|
|
440
|
-
}
|
|
441
240
|
// ─── Agent Config Push ───
|
|
241
|
+
//
|
|
242
|
+
// Knowledge Bases used to be pushed here (BEFORE entity writes) with a
|
|
243
|
+
// special pre-step that pushed integrations even earlier so KB uploads
|
|
244
|
+
// would find their integration. That ordering caused two production
|
|
245
|
+
// bugs: integrations were PATCHed before the parameters they reference
|
|
246
|
+
// existed (CTS-12425), and tools were pushed in parallel with their
|
|
247
|
+
// dependencies (CTS-12426). The KB push has been moved into the unified
|
|
248
|
+
// dependency-ordered flow below — see `pushKnowledgeHubs` invocation.
|
|
442
249
|
if (!diff || !diff.hasChanges) {
|
|
443
250
|
pushSpan.setAttribute("push.exit_reason", "no_agent_config_changes");
|
|
444
251
|
log(_jsx(Box, { paddingX: 1, children: _jsx(StatusLine, { kind: "success", label: "No agent config changes to push." }) }));
|
|
@@ -479,20 +286,10 @@ async function _push(pushSpan, agentCode, options = {}) {
|
|
|
479
286
|
// available drafts. Push is rejected if no draft is found.
|
|
480
287
|
let prePushDraft = null;
|
|
481
288
|
if (projectConfig.version_id || options.versionId) {
|
|
482
|
-
prePushDraft =
|
|
483
|
-
resolvedPushDraftCache ??
|
|
484
|
-
(await resolveVersionDraft(config, projectConfig, session, options.versionId));
|
|
289
|
+
prePushDraft = await resolveVersionDraft(config, projectConfig, session, options.versionId);
|
|
485
290
|
agentSettingsParams.version_id = prePushDraft.versionId;
|
|
486
|
-
agentSettingsParams.agent_id = prePushDraft.agentId;
|
|
487
|
-
cachedPushAgentManagementId = prePushDraft.agentId;
|
|
488
291
|
log(_jsx(StatusLine, { kind: "info", label: `Pushing into draft version: ${prePushDraft.label}` }));
|
|
489
292
|
}
|
|
490
|
-
else {
|
|
491
|
-
if (!cachedPushAgentManagementId) {
|
|
492
|
-
cachedPushAgentManagementId = await resolvePushAgentManagementId(config, projectConfig, session, projectRoot);
|
|
493
|
-
}
|
|
494
|
-
agentSettingsParams.agent_id = cachedPushAgentManagementId;
|
|
495
|
-
}
|
|
496
293
|
const pushTasks = buildPushTasks(diff, fileData, projectRoot, getFileDiff);
|
|
497
294
|
pushSpan.setAttribute("push.task_count", pushTasks.length);
|
|
498
295
|
if (diff) {
|
|
@@ -526,82 +323,55 @@ async function _push(pushSpan, agentCode, options = {}) {
|
|
|
526
323
|
};
|
|
527
324
|
const agentCodeStr = projectConfig.agent_code || projectConfig.agent_id || "unknown";
|
|
528
325
|
const agentIdStr = projectConfig.agent_id || "unknown";
|
|
529
|
-
|
|
326
|
+
/**
|
|
327
|
+
* Run one push step (a group of related tasks for one entity-type)
|
|
328
|
+
* STRICTLY SEQUENTIALLY. There is intentionally no `parallel` flag — the
|
|
329
|
+
* agent-settings backend has no optimistic locking and concurrent writes
|
|
330
|
+
* to the same agent silently merge / drop unresolvable references / re-
|
|
331
|
+
* sequence array items (see file header doc + CTS-12340 / -12425 / -12426
|
|
332
|
+
* for prior incidents). If you think you need to parallelize, you don't.
|
|
333
|
+
*/
|
|
334
|
+
const pushStep = async (tasks, label) => {
|
|
530
335
|
if (tasks.length === 0)
|
|
531
336
|
return true;
|
|
532
337
|
log(_jsx(Box, { paddingX: 1, children: _jsxs(Text, { color: colors.info, children: [icons.bullet, " ", label, "..."] }) }));
|
|
533
338
|
const stepFailed = [];
|
|
534
339
|
try {
|
|
535
|
-
|
|
536
|
-
const
|
|
537
|
-
|
|
538
|
-
|
|
539
|
-
|
|
540
|
-
|
|
541
|
-
|
|
542
|
-
|
|
543
|
-
|
|
544
|
-
|
|
545
|
-
|
|
546
|
-
|
|
547
|
-
else {
|
|
548
|
-
const err = results[i].reason;
|
|
549
|
-
if (isAuthError(err)) {
|
|
550
|
-
authFailed = true;
|
|
551
|
-
authFailedTasks.push(tasks[i]);
|
|
552
|
-
taskResult.status = "auth-failed";
|
|
553
|
-
}
|
|
554
|
-
else {
|
|
555
|
-
failed++;
|
|
556
|
-
const errMsg = err instanceof Error ? err.message : String(err);
|
|
557
|
-
const failure = {
|
|
558
|
-
label: tasks[i].label,
|
|
559
|
-
file: tasks[i].file,
|
|
560
|
-
error: errMsg,
|
|
561
|
-
};
|
|
562
|
-
pushFailures.push(failure);
|
|
563
|
-
stepFailed.push(failure);
|
|
564
|
-
taskResult.status = "failed";
|
|
565
|
-
taskResult.error = errMsg;
|
|
566
|
-
}
|
|
340
|
+
for (const task of tasks) {
|
|
341
|
+
const taskResult = {
|
|
342
|
+
label: task.label,
|
|
343
|
+
status: "success",
|
|
344
|
+
};
|
|
345
|
+
try {
|
|
346
|
+
const result = await executePushTask(client, agentSettingsParams, task);
|
|
347
|
+
succeeded++;
|
|
348
|
+
if (task.file)
|
|
349
|
+
succeededFiles.push(task.file);
|
|
350
|
+
if (isAlreadyAbsentResult(result)) {
|
|
351
|
+
taskResult.label = `${task.label} (already absent)`;
|
|
567
352
|
}
|
|
568
|
-
log(_jsx(Box, { paddingX: 2, children: _jsx(PushTaskLine, { result: taskResult }) }));
|
|
569
353
|
}
|
|
570
|
-
|
|
571
|
-
|
|
572
|
-
|
|
573
|
-
|
|
574
|
-
|
|
575
|
-
status: "success",
|
|
576
|
-
};
|
|
577
|
-
try {
|
|
578
|
-
await executePushTask(client, agentSettingsParams, task);
|
|
579
|
-
succeeded++;
|
|
580
|
-
if (task.file)
|
|
581
|
-
succeededFiles.push(task.file);
|
|
354
|
+
catch (err) {
|
|
355
|
+
if (isAuthError(err)) {
|
|
356
|
+
authFailed = true;
|
|
357
|
+
authFailedTasks.push(task);
|
|
358
|
+
taskResult.status = "auth-failed";
|
|
582
359
|
}
|
|
583
|
-
|
|
584
|
-
|
|
585
|
-
|
|
586
|
-
|
|
587
|
-
|
|
588
|
-
|
|
589
|
-
|
|
590
|
-
|
|
591
|
-
|
|
592
|
-
|
|
593
|
-
|
|
594
|
-
|
|
595
|
-
error: errMsg,
|
|
596
|
-
};
|
|
597
|
-
pushFailures.push(failure);
|
|
598
|
-
stepFailed.push(failure);
|
|
599
|
-
taskResult.status = "failed";
|
|
600
|
-
taskResult.error = errMsg;
|
|
601
|
-
}
|
|
360
|
+
else {
|
|
361
|
+
failed++;
|
|
362
|
+
const errMsg = err instanceof Error ? err.message : String(err);
|
|
363
|
+
const failure = {
|
|
364
|
+
label: task.label,
|
|
365
|
+
file: task.file,
|
|
366
|
+
error: errMsg,
|
|
367
|
+
};
|
|
368
|
+
pushFailures.push(failure);
|
|
369
|
+
stepFailed.push(failure);
|
|
370
|
+
taskResult.status = "failed";
|
|
371
|
+
taskResult.error = errMsg;
|
|
602
372
|
}
|
|
603
|
-
log(_jsx(Box, { paddingX: 2, children: _jsx(PushTaskLine, { result: taskResult }) }));
|
|
604
373
|
}
|
|
374
|
+
log(_jsx(Box, { paddingX: 2, children: _jsx(PushTaskLine, { result: taskResult }) }));
|
|
605
375
|
}
|
|
606
376
|
return stepFailed.length === 0 && !authFailed;
|
|
607
377
|
}
|
|
@@ -627,17 +397,45 @@ async function _push(pushSpan, agentCode, options = {}) {
|
|
|
627
397
|
t.type === "delete-tool");
|
|
628
398
|
const settingsTasks = pushTasks.filter((t) => t.type === "patch-general-settings");
|
|
629
399
|
const rulesTasks = pushTasks.filter((t) => t.type === "put-rules");
|
|
630
|
-
|
|
631
|
-
//
|
|
632
|
-
|
|
633
|
-
|
|
634
|
-
|
|
635
|
-
|
|
636
|
-
//
|
|
637
|
-
|
|
638
|
-
await pushStep(
|
|
639
|
-
|
|
640
|
-
await pushStep(
|
|
400
|
+
// ─── Push order — see file header for rationale ─────────────────────
|
|
401
|
+
//
|
|
402
|
+
// Phase 1: UPSERTS, top-down by dependency (least → most depends-on).
|
|
403
|
+
// Every step is sequential by construction (`pushStep` has no
|
|
404
|
+
// parallel mode). Do not work around this — the agent-settings
|
|
405
|
+
// backend silently merges / drops unresolvable refs on concurrent
|
|
406
|
+
// writes.
|
|
407
|
+
// 1. Parameters — referenced by entities, integrations, tools, rules.
|
|
408
|
+
await pushStep(paramTasks, "Pushing parameters");
|
|
409
|
+
// 2. Entities — reference parameters; referenced by tools, integrations.
|
|
410
|
+
await pushStep(entityTasks, "Pushing entities");
|
|
411
|
+
// 3. Integration upserts — reference parameters / entities. Must be
|
|
412
|
+
// pushed BEFORE knowledge-base uploads (KBs attach to integrations)
|
|
413
|
+
// AND before tools (tools reference integration codes).
|
|
414
|
+
await pushStep(integrationUpsertTasks, "Pushing integrations");
|
|
415
|
+
// 4. Knowledge Bases — reference integrations existing on the platform.
|
|
416
|
+
// KB failures are folded into the same `failed` counter / pushFailures
|
|
417
|
+
// array as agent-settings writes, so they hit the "X failed." line, the
|
|
418
|
+
// JSON envelope, and the non-zero exit code (BFF contract: zero silent
|
|
419
|
+
// errors anywhere in the push pipeline).
|
|
420
|
+
const kbResult = await pushKnowledgeHubs(projectRoot, projectConfig);
|
|
421
|
+
if (!kbResult.ok) {
|
|
422
|
+
for (const kbFailure of kbResult.failures) {
|
|
423
|
+
failed++;
|
|
424
|
+
pushFailures.push(kbFailure);
|
|
425
|
+
}
|
|
426
|
+
}
|
|
427
|
+
// 5. Tools — reference parameters, entities, integrations. Sequential:
|
|
428
|
+
// parallel tool pushes caused inter-tool race conditions in
|
|
429
|
+
// production (chain-activation, success-rule re-sequencing).
|
|
430
|
+
await pushStep(toolTasks, "Pushing tools");
|
|
431
|
+
// 6. Rules — reference tools, parameters, entities.
|
|
432
|
+
await pushStep(rulesTasks, "Pushing rules");
|
|
433
|
+
// 7. General settings — mostly standalone but may reference defaults.
|
|
434
|
+
await pushStep(settingsTasks, "Pushing general settings");
|
|
435
|
+
// Phase 2: DELETES, bottom-up. Integrations get deleted last so any
|
|
436
|
+
// tool / entity update above that still referenced them succeeds first.
|
|
437
|
+
await pushStep(integrationDeleteTasks, "Removing integrations");
|
|
438
|
+
// Phase 3: Snapshot — runs at the very end of `_push` (see below).
|
|
641
439
|
// Auth fallback
|
|
642
440
|
if (authFailed && authFailedTasks.length > 0 && !savedApiKey) {
|
|
643
441
|
// The auth fallback prompts for an API key. In a non-TTY environment
|
|
@@ -795,12 +593,29 @@ async function _push(pushSpan, agentCode, options = {}) {
|
|
|
795
593
|
// This ensures: if snapshot fails, user re-runs `aui push` to retry both
|
|
796
594
|
// failed entity pushes AND the snapshot. Local files remain the source
|
|
797
595
|
// of truth until the server has captured them.
|
|
596
|
+
//
|
|
597
|
+
// CRITICAL (CTS-12340 follow-up): when one file has BOTH succeeded and
|
|
598
|
+
// failed tasks (e.g. integrations.aui.json with a successful DELETE on
|
|
599
|
+
// web-search and a failed POST on search-flights), do NOT commit that
|
|
600
|
+
// file to baseline. If we did, the next push's git diff would treat the
|
|
601
|
+
// failed item as "already on the platform" and emit a PATCH that 404s.
|
|
602
|
+
// The previous behaviour stuck users in an unrecoverable retry loop.
|
|
798
603
|
let baselineUpdated = false;
|
|
799
604
|
const canCommitBaseline = !prePushDraft || snapshotSucceeded;
|
|
800
605
|
if (canCommitBaseline) {
|
|
606
|
+
// A file is committable iff EVERY task that targeted it succeeded.
|
|
607
|
+
// Build the failed-files set from `pushFailures` (which now includes
|
|
608
|
+
// both agent-settings entity failures AND knowledge-hub failures —
|
|
609
|
+
// see the KB push step).
|
|
610
|
+
const failedFiles = new Set();
|
|
611
|
+
for (const f of pushFailures) {
|
|
612
|
+
if (f.file)
|
|
613
|
+
failedFiles.add(f.file);
|
|
614
|
+
}
|
|
615
|
+
const filesSafeToCommit = succeededFiles.filter((f) => !failedFiles.has(f));
|
|
801
616
|
if (failed > 0 && succeeded > 0) {
|
|
802
|
-
if (
|
|
803
|
-
commitBaselineFiles(projectRoot,
|
|
617
|
+
if (filesSafeToCommit.length > 0) {
|
|
618
|
+
commitBaselineFiles(projectRoot, filesSafeToCommit, `pushed ${succeeded} change(s) (${failedFiles.size} file(s) held back due to per-task failures)`);
|
|
804
619
|
baselineUpdated = true;
|
|
805
620
|
}
|
|
806
621
|
}
|
|
@@ -902,13 +717,11 @@ async function _push(pushSpan, agentCode, options = {}) {
|
|
|
902
717
|
throw error;
|
|
903
718
|
}
|
|
904
719
|
}
|
|
905
|
-
|
|
906
|
-
|
|
907
|
-
|
|
908
|
-
|
|
909
|
-
|
|
910
|
-
*/
|
|
911
|
-
async function lookupAgentManagementInfoForPush(config, projectConfig, session) {
|
|
720
|
+
async function resolveVersionDraft(config, projectConfig, session, explicitVersionId) {
|
|
721
|
+
// Every error path below MUST throw a typed CLIError (not return null).
|
|
722
|
+
// Returning null silently exits the CLI with code 0 — the BFF then thinks
|
|
723
|
+
// the push succeeded when nothing actually happened, and the failure
|
|
724
|
+
// never reaches Logfire because no exception bubbled to handleError.
|
|
912
725
|
const client = new AUIClient({
|
|
913
726
|
baseUrl: config.apiUrl,
|
|
914
727
|
authToken: config.authToken,
|
|
@@ -920,98 +733,55 @@ async function lookupAgentManagementInfoForPush(config, projectConfig, session)
|
|
|
920
733
|
if (key)
|
|
921
734
|
client.setAgentSettingsApiKey(key);
|
|
922
735
|
let agentInfo;
|
|
923
|
-
const errors = [];
|
|
924
736
|
const agentMgmtId = session.agent_management_id;
|
|
737
|
+
// Project's network_id (from .auirc) takes priority over session — when
|
|
738
|
+
// you're inside a project, that's the agent you mean. Session agent may
|
|
739
|
+
// point at a different agent (e.g. last `aui agents --switch`).
|
|
925
740
|
const projectNetworkId = projectConfig.agent_id;
|
|
926
741
|
const fallbackNetworkId = session.network_id;
|
|
927
742
|
if (projectNetworkId) {
|
|
928
743
|
try {
|
|
929
744
|
const resp = await client.agentManagement.listAgents(client.getOrganizationId(), 1, 50, { network_id: projectNetworkId });
|
|
930
|
-
agentInfo = resp.items.find((a) => a.scope.network_id === projectNetworkId ||
|
|
931
|
-
a.id === projectNetworkId);
|
|
932
|
-
if (!agentInfo) {
|
|
933
|
-
errors.push(`listAgents(network_id=${projectNetworkId}) returned ${resp.items.length} item(s), none matched.`);
|
|
934
|
-
}
|
|
745
|
+
agentInfo = resp.items.find((a) => a.scope.network_id === projectNetworkId || a.id === projectNetworkId);
|
|
935
746
|
}
|
|
936
747
|
catch (err) {
|
|
937
|
-
|
|
748
|
+
// Listing fall-through is fine because the next two branches try other
|
|
749
|
+
// resolution paths AND a final ConfigError is thrown below if none
|
|
750
|
+
// succeed. But emit a debug warning so an operator with AUI_DEBUG=1
|
|
751
|
+
// can see WHICH branch failed and why (zero silent errors policy).
|
|
752
|
+
if (process.env.AUI_DEBUG) {
|
|
753
|
+
console.warn(`[debug] resolveVersionDraft: listAgents(network_id=${projectNetworkId}) failed:`, err instanceof Error ? err.message : err);
|
|
754
|
+
}
|
|
938
755
|
}
|
|
939
756
|
}
|
|
940
|
-
//
|
|
941
|
-
|
|
942
|
-
// the case where listAgents fell through above.
|
|
943
|
-
if (!agentInfo && agentMgmtId) {
|
|
757
|
+
// Fall back to session's agent_management_id only when not inside a project
|
|
758
|
+
if (!agentInfo && !projectNetworkId && agentMgmtId) {
|
|
944
759
|
try {
|
|
945
760
|
agentInfo = await client.agentManagement.getAgent(agentMgmtId);
|
|
946
761
|
}
|
|
947
762
|
catch (err) {
|
|
948
|
-
|
|
763
|
+
if (process.env.AUI_DEBUG) {
|
|
764
|
+
console.warn(`[debug] resolveVersionDraft: getAgent(${agentMgmtId}) failed (stale id?):`, err instanceof Error ? err.message : err);
|
|
765
|
+
}
|
|
949
766
|
}
|
|
950
767
|
}
|
|
951
|
-
|
|
768
|
+
// Last resort: session's network_id
|
|
769
|
+
if (!agentInfo && fallbackNetworkId) {
|
|
952
770
|
try {
|
|
953
771
|
const resp = await client.agentManagement.listAgents(client.getOrganizationId(), 1, 50, { network_id: fallbackNetworkId });
|
|
954
|
-
agentInfo = resp.items.find((a) => a.scope.network_id === fallbackNetworkId ||
|
|
955
|
-
a.id === fallbackNetworkId);
|
|
956
|
-
if (!agentInfo) {
|
|
957
|
-
errors.push(`listAgents(network_id=${fallbackNetworkId}) returned ${resp.items.length} item(s), none matched.`);
|
|
958
|
-
}
|
|
772
|
+
agentInfo = resp.items.find((a) => a.scope.network_id === fallbackNetworkId || a.id === fallbackNetworkId);
|
|
959
773
|
}
|
|
960
774
|
catch (err) {
|
|
961
|
-
|
|
775
|
+
if (process.env.AUI_DEBUG) {
|
|
776
|
+
console.warn(`[debug] resolveVersionDraft: listAgents(network_id=${fallbackNetworkId}) failed:`, err instanceof Error ? err.message : err);
|
|
777
|
+
}
|
|
962
778
|
}
|
|
963
779
|
}
|
|
964
|
-
return { agentInfo, errors };
|
|
965
|
-
}
|
|
966
|
-
/**
|
|
967
|
-
* Return the agent-management UUID to send as `agent_id` on agent-settings
|
|
968
|
-
* write bodies. Reads `.auirc` first; falls back to `lookupAgentManagementInfoForPush`
|
|
969
|
-
* and **persists** the resolved id back to `.auirc` so subsequent pushes don't
|
|
970
|
-
* pay the lookup cost. Throws `ConfigError` if no id can be resolved — never
|
|
971
|
-
* silently returns undefined, because that's how entities ended up in the DB
|
|
972
|
-
* without `agent_id`.
|
|
973
|
-
*/
|
|
974
|
-
async function resolvePushAgentManagementId(config, projectConfig, session, projectRoot) {
|
|
975
|
-
if (projectConfig.agent_management_id)
|
|
976
|
-
return projectConfig.agent_management_id;
|
|
977
|
-
const { agentInfo, errors } = await lookupAgentManagementInfoForPush(config, projectConfig, session);
|
|
978
|
-
if (!agentInfo) {
|
|
979
|
-
const detail = errors.length > 0 ? `\n - ${errors.join("\n - ")}` : "";
|
|
980
|
-
throw new ConfigError(`Could not resolve agent-management id for this project.${detail}`, {
|
|
981
|
-
suggestion: "Re-run `aui import-agent` (will populate .auirc.agent_management_id) or `aui pull` to back-fill it.",
|
|
982
|
-
});
|
|
983
|
-
}
|
|
984
|
-
// Migrate legacy projects: persist back so the next push skips the lookup.
|
|
985
|
-
try {
|
|
986
|
-
saveProjectConfig({ ...projectConfig, agent_management_id: agentInfo.id }, projectRoot);
|
|
987
|
-
}
|
|
988
|
-
catch {
|
|
989
|
-
// .auirc write failure is non-fatal — we already have the id in memory.
|
|
990
|
-
}
|
|
991
|
-
return agentInfo.id;
|
|
992
|
-
}
|
|
993
|
-
async function resolveVersionDraft(config, projectConfig, session, explicitVersionId) {
|
|
994
|
-
// Every error path below MUST throw a typed CLIError (not return null).
|
|
995
|
-
// Returning null silently exits the CLI with code 0 — the BFF then thinks
|
|
996
|
-
// the push succeeded when nothing actually happened, and the failure
|
|
997
|
-
// never reaches Logfire because no exception bubbled to handleError.
|
|
998
|
-
const { agentInfo, errors: lookupErrors } = await lookupAgentManagementInfoForPush(config, projectConfig, session);
|
|
999
780
|
if (!agentInfo) {
|
|
1000
|
-
|
|
1001
|
-
throw new ConfigError(`Could not resolve agent for version management.${detail}`, {
|
|
781
|
+
throw new ConfigError("Could not resolve agent for version management.", {
|
|
1002
782
|
suggestion: "Run `aui import-agent` to link an agent, or check your session with `aui status`.",
|
|
1003
783
|
});
|
|
1004
784
|
}
|
|
1005
|
-
const client = new AUIClient({
|
|
1006
|
-
baseUrl: config.apiUrl,
|
|
1007
|
-
authToken: config.authToken,
|
|
1008
|
-
accountId: config.accountId,
|
|
1009
|
-
organizationId: config.organizationId,
|
|
1010
|
-
environment: config.environment,
|
|
1011
|
-
});
|
|
1012
|
-
const key = loadAgentSettingsApiKey();
|
|
1013
|
-
if (key)
|
|
1014
|
-
client.setAgentSettingsApiKey(key);
|
|
1015
785
|
// If user passed --version-id, validate it's a draft
|
|
1016
786
|
if (explicitVersionId) {
|
|
1017
787
|
let ver;
|
|
@@ -1146,8 +916,14 @@ async function resolveAgentSettingsParams(config, projectConfig, session, projec
|
|
|
1146
916
|
saveProjectConfig({ ...projectConfig, network_category_id: categoryId }, projectRoot);
|
|
1147
917
|
}
|
|
1148
918
|
}
|
|
1149
|
-
catch {
|
|
1150
|
-
//
|
|
919
|
+
catch (err) {
|
|
920
|
+
// Falls through to the explicit ConfigError below if no categoryId
|
|
921
|
+
// could be resolved. Surface in AUI_DEBUG so the operator can see
|
|
922
|
+
// why the auto-fetch failed instead of just the generic "Missing
|
|
923
|
+
// network_category_id" message.
|
|
924
|
+
if (process.env.AUI_DEBUG) {
|
|
925
|
+
console.warn(`[debug] resolveAgentSettingsParams: networks.get(${networkId}) failed:`, err instanceof Error ? err.message : err);
|
|
926
|
+
}
|
|
1151
927
|
}
|
|
1152
928
|
}
|
|
1153
929
|
if (!categoryId) {
|
|
@@ -1182,6 +958,215 @@ async function resolveAgentSettingsParams(config, projectConfig, session, projec
|
|
|
1182
958
|
}
|
|
1183
959
|
return baseParams;
|
|
1184
960
|
}
|
|
961
|
+
// ─── Push Task Classification Helpers ───
|
|
962
|
+
/**
|
|
963
|
+
* Integration tasks split into two phases by the unified push order:
|
|
964
|
+
* - Upserts (POST/PATCH/PUT) run BEFORE knowledge bases + tools, so KBs
|
|
965
|
+
* can attach to integrations and tools can reference integration codes.
|
|
966
|
+
* - Deletes run AFTER tools / entities, so the last write that mentioned
|
|
967
|
+
* the integration succeeds before the row is removed.
|
|
968
|
+
*/
|
|
969
|
+
function isIntegrationUpsertTask(t) {
|
|
970
|
+
return (t.type === "put-integrations" ||
|
|
971
|
+
t.type === "create-integration" ||
|
|
972
|
+
t.type === "patch-integration");
|
|
973
|
+
}
|
|
974
|
+
async function pushKnowledgeHubs(projectRoot, projectConfig) {
|
|
975
|
+
const { getKnowledgeHubChanges } = await import("../utils/git.js");
|
|
976
|
+
const kbChanges = getKnowledgeHubChanges(projectRoot);
|
|
977
|
+
if (kbChanges.length === 0)
|
|
978
|
+
return { ok: true, failures: [] };
|
|
979
|
+
const kbConfig = getConfig();
|
|
980
|
+
const kbSession = await getValidSession();
|
|
981
|
+
const kbNetworkId = projectConfig.agent_id || kbSession?.network_id;
|
|
982
|
+
if (!kbNetworkId || !kbConfig.authToken) {
|
|
983
|
+
return {
|
|
984
|
+
ok: false,
|
|
985
|
+
failures: [
|
|
986
|
+
{
|
|
987
|
+
label: "Push knowledge hubs",
|
|
988
|
+
error: "Cannot push knowledge hubs: missing network_id or auth token. Re-run `aui login` and `aui import-agent`.",
|
|
989
|
+
},
|
|
990
|
+
],
|
|
991
|
+
};
|
|
992
|
+
}
|
|
993
|
+
const { KBViewClient } = await import("../api-client/kb-view-client.js");
|
|
994
|
+
const { buildScope, readKbFolder } = await import("../services/kb-view.service.js");
|
|
995
|
+
const { loadAgentSettingsApiKey: loadAsKey } = await import("../config/index.js");
|
|
996
|
+
const kbViewClient = new KBViewClient({
|
|
997
|
+
authToken: kbConfig.authToken,
|
|
998
|
+
apiKey: loadAsKey() || undefined,
|
|
999
|
+
organizationId: kbConfig.organizationId || "",
|
|
1000
|
+
environment: kbConfig.environment || "staging",
|
|
1001
|
+
});
|
|
1002
|
+
const kbLogDir = path.join(projectRoot, ".aui", "push-logs");
|
|
1003
|
+
fs.mkdirSync(kbLogDir, { recursive: true });
|
|
1004
|
+
kbViewClient.setPushLogDir(kbLogDir);
|
|
1005
|
+
const scope = buildScope({
|
|
1006
|
+
networkId: kbNetworkId,
|
|
1007
|
+
organizationId: projectConfig.organization_id || kbConfig.organizationId || "",
|
|
1008
|
+
accountId: projectConfig.account_id || kbConfig.accountId || "",
|
|
1009
|
+
});
|
|
1010
|
+
const userId = kbSession?.user_id || "cli";
|
|
1011
|
+
const changedKBDirs = new Set();
|
|
1012
|
+
for (const change of kbChanges) {
|
|
1013
|
+
if (change.kbDirName)
|
|
1014
|
+
changedKBDirs.add(change.kbDirName);
|
|
1015
|
+
}
|
|
1016
|
+
const existingKBDirs = [...changedKBDirs].filter((d) => fs.existsSync(path.join(projectRoot, "knowledge-hubs", d)));
|
|
1017
|
+
const deletedKBDirs = [...changedKBDirs].filter((d) => !fs.existsSync(path.join(projectRoot, "knowledge-hubs", d)));
|
|
1018
|
+
const failures = [];
|
|
1019
|
+
let kbDeleteSucceeded = true;
|
|
1020
|
+
if (deletedKBDirs.length > 0) {
|
|
1021
|
+
const { getBaselineFileContent } = await import("../utils/git.js");
|
|
1022
|
+
const deleteSpinner = startSpinner(`Deleting ${deletedKBDirs.length} knowledge base(s) from server...`);
|
|
1023
|
+
try {
|
|
1024
|
+
for (const kbDirName of deletedKBDirs) {
|
|
1025
|
+
const baselineKb = getBaselineFileContent(projectRoot, `knowledge-hubs/${kbDirName}/kb.json`);
|
|
1026
|
+
const kbName = baselineKb?.name || kbDirName;
|
|
1027
|
+
const kbId = baselineKb?.knowledge_base_id;
|
|
1028
|
+
if (!kbId) {
|
|
1029
|
+
log(_jsx(Box, { paddingX: 1, children: _jsx(StatusLine, { kind: "warning", label: `Cannot delete "${kbName}" — no knowledge_base_id stored. Push the KB first, then delete.` }) }));
|
|
1030
|
+
continue;
|
|
1031
|
+
}
|
|
1032
|
+
try {
|
|
1033
|
+
await kbViewClient.deleteKnowledgeBase(kbId, scope, kbName);
|
|
1034
|
+
log(_jsx(Box, { paddingX: 1, children: _jsx(StatusLine, { kind: "success", label: `Deleted: ${kbName}` }) }));
|
|
1035
|
+
}
|
|
1036
|
+
catch (delErr) {
|
|
1037
|
+
// Per-KB error: count it, keep going so partial work shows up.
|
|
1038
|
+
if (isNotFoundError(delErr)) {
|
|
1039
|
+
log(_jsx(Box, { paddingX: 1, children: _jsx(StatusLine, { kind: "success", label: `Deleted: ${kbName} (already absent)` }) }));
|
|
1040
|
+
}
|
|
1041
|
+
else {
|
|
1042
|
+
kbDeleteSucceeded = false;
|
|
1043
|
+
const errMsg = delErr instanceof Error ? delErr.message : String(delErr);
|
|
1044
|
+
failures.push({
|
|
1045
|
+
label: `Delete knowledge base: ${kbName}`,
|
|
1046
|
+
file: `knowledge-hubs/${kbDirName}/kb.json`,
|
|
1047
|
+
error: errMsg,
|
|
1048
|
+
});
|
|
1049
|
+
log(_jsx(Box, { paddingX: 1, children: _jsx(StatusLine, { kind: "error", label: `Failed to delete "${kbName}": ${errMsg}` }) }));
|
|
1050
|
+
}
|
|
1051
|
+
}
|
|
1052
|
+
}
|
|
1053
|
+
if (kbDeleteSucceeded) {
|
|
1054
|
+
deleteSpinner.succeed(`${deletedKBDirs.length} knowledge base(s) deleted`);
|
|
1055
|
+
}
|
|
1056
|
+
else {
|
|
1057
|
+
deleteSpinner.fail(`Knowledge base deletion completed with errors`);
|
|
1058
|
+
}
|
|
1059
|
+
}
|
|
1060
|
+
catch (error) {
|
|
1061
|
+
kbDeleteSucceeded = false;
|
|
1062
|
+
deleteSpinner.fail("Knowledge base deletion failed");
|
|
1063
|
+
const errMsg = error instanceof Error ? error.message : String(error);
|
|
1064
|
+
failures.push({
|
|
1065
|
+
label: "Delete knowledge bases (batch)",
|
|
1066
|
+
error: errMsg,
|
|
1067
|
+
});
|
|
1068
|
+
log(_jsx(ErrorDisplay, { error: error }));
|
|
1069
|
+
}
|
|
1070
|
+
}
|
|
1071
|
+
let kbUploadSucceeded = false;
|
|
1072
|
+
if (existingKBDirs.length > 0) {
|
|
1073
|
+
const kbSpinner = startSpinner(`Pushing ${existingKBDirs.length} knowledge base(s)...`);
|
|
1074
|
+
let hadUploadFailure = false;
|
|
1075
|
+
try {
|
|
1076
|
+
for (const kbDirName of existingKBDirs) {
|
|
1077
|
+
const kbDir = path.join(projectRoot, "knowledge-hubs", kbDirName);
|
|
1078
|
+
const kbData = readKbFolder(kbDir);
|
|
1079
|
+
if (!kbData)
|
|
1080
|
+
continue;
|
|
1081
|
+
const SUPPORTED_EXTENSIONS = new Set([".pdf", ".md", ".txt", ".json"]);
|
|
1082
|
+
const supportedFiles = kbData.binaryFiles.filter((f) => SUPPORTED_EXTENSIONS.has(path.extname(f).toLowerCase()));
|
|
1083
|
+
const skippedFiles = kbData.binaryFiles.filter((f) => !SUPPORTED_EXTENSIONS.has(path.extname(f).toLowerCase()));
|
|
1084
|
+
for (const skipped of skippedFiles) {
|
|
1085
|
+
log(_jsx(Box, { paddingX: 1, children: _jsx(StatusLine, { kind: "warning", label: `Skipped unsupported file: ${path.basename(skipped)} (only .pdf, .md, .txt, .json)` }) }));
|
|
1086
|
+
}
|
|
1087
|
+
if (supportedFiles.length > 0) {
|
|
1088
|
+
try {
|
|
1089
|
+
const importResult = await kbViewClient.importFiles({
|
|
1090
|
+
files: supportedFiles,
|
|
1091
|
+
scope,
|
|
1092
|
+
created_by: userId,
|
|
1093
|
+
knowledge_base_name: kbData.name,
|
|
1094
|
+
knowledge_base_description: kbData.description,
|
|
1095
|
+
});
|
|
1096
|
+
if (importResult.knowledge_base_id) {
|
|
1097
|
+
const kbJsonPath = path.join(kbDir, "kb.json");
|
|
1098
|
+
try {
|
|
1099
|
+
const raw = JSON.parse(fs.readFileSync(kbJsonPath, "utf-8"));
|
|
1100
|
+
raw.knowledge_base_id = importResult.knowledge_base_id;
|
|
1101
|
+
fs.writeFileSync(kbJsonPath, JSON.stringify(raw, null, 2) + "\n");
|
|
1102
|
+
}
|
|
1103
|
+
catch (writeErr) {
|
|
1104
|
+
// kb.json id write fail is non-fatal but tell the user so the
|
|
1105
|
+
// next push doesn't surprise them with "no knowledge_base_id stored".
|
|
1106
|
+
if (process.env.AUI_DEBUG) {
|
|
1107
|
+
console.warn(`[debug] failed to write knowledge_base_id back to ${kbJsonPath}:`, writeErr);
|
|
1108
|
+
}
|
|
1109
|
+
log(_jsx(Box, { paddingX: 1, children: _jsx(StatusLine, { kind: "warning", label: `Could not persist knowledge_base_id back to ${path.basename(kbJsonPath)} — re-import or run \`aui pull\` to recover.` }) }));
|
|
1110
|
+
}
|
|
1111
|
+
}
|
|
1112
|
+
}
|
|
1113
|
+
catch (uploadErr) {
|
|
1114
|
+
hadUploadFailure = true;
|
|
1115
|
+
const errMsg = uploadErr instanceof Error ? uploadErr.message : String(uploadErr);
|
|
1116
|
+
failures.push({
|
|
1117
|
+
label: `Push knowledge base: ${kbData.name || kbDirName}`,
|
|
1118
|
+
file: `knowledge-hubs/${kbDirName}/kb.json`,
|
|
1119
|
+
error: errMsg,
|
|
1120
|
+
});
|
|
1121
|
+
log(_jsx(Box, { paddingX: 1, children: _jsx(StatusLine, { kind: "error", label: `Failed to push "${kbData.name || kbDirName}": ${errMsg}` }) }));
|
|
1122
|
+
}
|
|
1123
|
+
}
|
|
1124
|
+
}
|
|
1125
|
+
if (hadUploadFailure) {
|
|
1126
|
+
kbSpinner.fail(`Knowledge base push completed with errors`);
|
|
1127
|
+
kbUploadSucceeded = false;
|
|
1128
|
+
}
|
|
1129
|
+
else {
|
|
1130
|
+
kbSpinner.succeed(`Knowledge base(s) pushed`);
|
|
1131
|
+
kbUploadSucceeded = true;
|
|
1132
|
+
}
|
|
1133
|
+
}
|
|
1134
|
+
catch (error) {
|
|
1135
|
+
kbSpinner.fail("Knowledge base push failed");
|
|
1136
|
+
const errMsg = error instanceof Error ? error.message : String(error);
|
|
1137
|
+
failures.push({
|
|
1138
|
+
label: "Push knowledge bases (batch)",
|
|
1139
|
+
error: errMsg,
|
|
1140
|
+
});
|
|
1141
|
+
log(_jsx(ErrorDisplay, { error: error }));
|
|
1142
|
+
}
|
|
1143
|
+
}
|
|
1144
|
+
else {
|
|
1145
|
+
kbUploadSucceeded = true;
|
|
1146
|
+
}
|
|
1147
|
+
const kbPushSucceeded = kbUploadSucceeded && kbDeleteSucceeded;
|
|
1148
|
+
if (kbPushSucceeded) {
|
|
1149
|
+
const kbFilesToAdd = kbChanges
|
|
1150
|
+
.filter((c) => c.status !== "deleted")
|
|
1151
|
+
.map((c) => c.file);
|
|
1152
|
+
const kbFilesToDelete = kbChanges
|
|
1153
|
+
.filter((c) => c.status === "deleted")
|
|
1154
|
+
.map((c) => c.file);
|
|
1155
|
+
if (kbFilesToAdd.length > 0 || kbFilesToDelete.length > 0) {
|
|
1156
|
+
const { commitBaselineFiles: commitKBFiles, removeBaselineFiles } = await import("../utils/git.js");
|
|
1157
|
+
if (kbFilesToDelete.length > 0) {
|
|
1158
|
+
removeBaselineFiles(projectRoot, kbFilesToDelete);
|
|
1159
|
+
}
|
|
1160
|
+
if (kbFilesToAdd.length > 0) {
|
|
1161
|
+
commitKBFiles(projectRoot, kbFilesToAdd, "pushed knowledge hub changes");
|
|
1162
|
+
}
|
|
1163
|
+
else {
|
|
1164
|
+
commitKBFiles(projectRoot, [], "removed knowledge hub files");
|
|
1165
|
+
}
|
|
1166
|
+
}
|
|
1167
|
+
}
|
|
1168
|
+
return { ok: kbPushSucceeded && failures.length === 0, failures };
|
|
1169
|
+
}
|
|
1185
1170
|
// ─── Array File Info Helper ───
|
|
1186
1171
|
function getArrayFileInfoForPush(filePath, dir) {
|
|
1187
1172
|
try {
|
|
@@ -1210,11 +1195,6 @@ function getArrayFileInfoForPush(filePath, dir) {
|
|
|
1210
1195
|
return null;
|
|
1211
1196
|
}
|
|
1212
1197
|
}
|
|
1213
|
-
function isIntegrationUpsertTask(t) {
|
|
1214
|
-
return (t.type === "put-integrations" ||
|
|
1215
|
-
t.type === "create-integration" ||
|
|
1216
|
-
t.type === "patch-integration");
|
|
1217
|
-
}
|
|
1218
1198
|
function writePushMemory(projectRoot, agentCode, agentId, pushTasks, succeededFiles, pushFailures) {
|
|
1219
1199
|
try {
|
|
1220
1200
|
const memoryDir = path.join(projectRoot, "memory");
|
|
@@ -1304,7 +1284,13 @@ function writePushMemory(projectRoot, agentCode, agentId, pushTasks, succeededFi
|
|
|
1304
1284
|
fs.writeFileSync(filePath, lines.join("\n"), "utf-8");
|
|
1305
1285
|
return path.relative(projectRoot, filePath);
|
|
1306
1286
|
}
|
|
1307
|
-
catch {
|
|
1287
|
+
catch (err) {
|
|
1288
|
+
// Memory file is diagnostic only — its failure shouldn't block the push.
|
|
1289
|
+
// But emit a debug warning so an operator chasing "where's my push memory"
|
|
1290
|
+
// sees what went wrong.
|
|
1291
|
+
if (process.env.AUI_DEBUG) {
|
|
1292
|
+
console.warn("[debug] writePushMemory failed:", err instanceof Error ? err.message : err);
|
|
1293
|
+
}
|
|
1308
1294
|
return undefined;
|
|
1309
1295
|
}
|
|
1310
1296
|
}
|
|
@@ -1594,95 +1580,280 @@ async function executePushTask(client, params, task) {
|
|
|
1594
1580
|
}
|
|
1595
1581
|
});
|
|
1596
1582
|
}
|
|
1597
|
-
//
|
|
1598
|
-
//
|
|
1599
|
-
//
|
|
1600
|
-
//
|
|
1583
|
+
// ─── Adaptive fallback matrix (per write task) ────────────────────────────
|
|
1584
|
+
//
|
|
1585
|
+
// Every entity write goes through three layers, applied in this order:
|
|
1586
|
+
//
|
|
1587
|
+
// (a) `withTransientRetry` — retries once on 500/502/503/504 with a 1s
|
|
1588
|
+
// back-off. Per-call, isolated from other
|
|
1589
|
+
// tasks. 4xx is never retried (deterministic).
|
|
1590
|
+
// (b) `POST 409 → PATCH` — the create call hit a row with the same
|
|
1591
|
+
// code; the platform already has it. Convert
|
|
1592
|
+
// to a PATCH and continue. Pre-existing.
|
|
1593
|
+
// (c) `PATCH 404 → POST` — the patch call hit "not found"; baseline
|
|
1594
|
+
// drifted (item never landed on the platform
|
|
1595
|
+
// from a prior partial push). Convert to a
|
|
1596
|
+
// POST so the row reappears. NEW.
|
|
1597
|
+
// (d) `DELETE 404 → success` — the delete target is already gone. The
|
|
1598
|
+
// desired end state is reached. Treat as
|
|
1599
|
+
// success and log "(already absent)" so the
|
|
1600
|
+
// user can see what happened. NEW.
|
|
1601
|
+
//
|
|
1602
|
+
// All four layers are visible in the per-task push log files under
|
|
1603
|
+
// `.aui/push-logs/` so the BFF / agent-builder-bff can audit decisions.
|
|
1601
1604
|
function isAlreadyExistsConflict(err) {
|
|
1602
1605
|
if (!err || typeof err !== "object")
|
|
1603
1606
|
return false;
|
|
1604
|
-
const
|
|
1607
|
+
const code = err.statusCode
|
|
1608
|
+
?? err.status;
|
|
1609
|
+
return code === 409;
|
|
1610
|
+
}
|
|
1611
|
+
function isNotFoundError(err) {
|
|
1612
|
+
if (!err || typeof err !== "object")
|
|
1613
|
+
return false;
|
|
1614
|
+
const code = err.statusCode
|
|
1605
1615
|
?? err.status;
|
|
1606
|
-
return
|
|
1616
|
+
return code === 404;
|
|
1617
|
+
}
|
|
1618
|
+
function isTransient5xx(err) {
|
|
1619
|
+
if (!err || typeof err !== "object")
|
|
1620
|
+
return false;
|
|
1621
|
+
const code = err.statusCode
|
|
1622
|
+
?? err.status;
|
|
1623
|
+
return code === 500 || code === 502 || code === 503 || code === 504;
|
|
1624
|
+
}
|
|
1625
|
+
/**
|
|
1626
|
+
* Run one entity-settings write call once, and retry exactly once on a
|
|
1627
|
+
* transient 5xx after a 1s back-off. The snapshot upload has its own
|
|
1628
|
+
* retry loop (see `pushSnapshot`); this is the equivalent for individual
|
|
1629
|
+
* agent-settings writes. Never retries on 4xx — those are deterministic.
|
|
1630
|
+
*/
|
|
1631
|
+
async function withTransientRetry(label, fn) {
|
|
1632
|
+
try {
|
|
1633
|
+
return await fn();
|
|
1634
|
+
}
|
|
1635
|
+
catch (err) {
|
|
1636
|
+
if (!isTransient5xx(err))
|
|
1637
|
+
throw err;
|
|
1638
|
+
const code = err.statusCode
|
|
1639
|
+
?? err.status;
|
|
1640
|
+
if (process.env.AUI_DEBUG) {
|
|
1641
|
+
console.log(`[debug] ${label} got ${code}, retrying once after 1000ms`);
|
|
1642
|
+
}
|
|
1643
|
+
await new Promise((r) => setTimeout(r, 1000));
|
|
1644
|
+
return await fn();
|
|
1645
|
+
}
|
|
1646
|
+
}
|
|
1647
|
+
/**
|
|
1648
|
+
* A delete that has been short-circuited because the row was already absent
|
|
1649
|
+
* on the platform. Returned as a successful resolution so callers don't
|
|
1650
|
+
* count the task as failed, but tagged so the per-task log line can show
|
|
1651
|
+
* "(already absent)" instead of a generic ✓.
|
|
1652
|
+
*/
|
|
1653
|
+
const DELETE_ALREADY_ABSENT = Object.freeze({
|
|
1654
|
+
__aui_already_absent__: true,
|
|
1655
|
+
message: "Already absent on platform — treated as success",
|
|
1656
|
+
});
|
|
1657
|
+
function isAlreadyAbsentResult(value) {
|
|
1658
|
+
return (!!value
|
|
1659
|
+
&& typeof value === "object"
|
|
1660
|
+
&& value.__aui_already_absent__ === true);
|
|
1607
1661
|
}
|
|
1608
1662
|
async function _executePushTask(client, params, task) {
|
|
1609
1663
|
switch (task.type) {
|
|
1610
1664
|
case "patch-tool":
|
|
1611
|
-
return
|
|
1612
|
-
|
|
1613
|
-
|
|
1614
|
-
|
|
1615
|
-
|
|
1616
|
-
|
|
1617
|
-
|
|
1618
|
-
|
|
1619
|
-
|
|
1665
|
+
return withTransientRetry(`PATCH tool ${task.toolName}`, async () => {
|
|
1666
|
+
try {
|
|
1667
|
+
return await client.patchTool(params, task.toolName, task.body);
|
|
1668
|
+
}
|
|
1669
|
+
catch (err) {
|
|
1670
|
+
if (isNotFoundError(err)) {
|
|
1671
|
+
if (process.env.AUI_DEBUG) {
|
|
1672
|
+
console.log(`[debug] patch-tool ${task.toolName}: 404 not found, falling back to POST`);
|
|
1673
|
+
}
|
|
1674
|
+
return client.createTool(params, task.body);
|
|
1620
1675
|
}
|
|
1621
|
-
|
|
1622
|
-
const toolCode = body.code || "";
|
|
1623
|
-
const toolName = toolCode.toUpperCase().replace(/-/g, "_");
|
|
1624
|
-
return client.patchTool(params, toolName, body);
|
|
1676
|
+
throw err;
|
|
1625
1677
|
}
|
|
1626
|
-
|
|
1627
|
-
|
|
1628
|
-
|
|
1678
|
+
});
|
|
1679
|
+
case "create-tool":
|
|
1680
|
+
return withTransientRetry(`POST tool ${task.toolName ?? task.itemCode}`, async () => {
|
|
1681
|
+
try {
|
|
1682
|
+
return await client.createTool(params, task.body);
|
|
1629
1683
|
}
|
|
1630
|
-
|
|
1631
|
-
|
|
1684
|
+
catch (err) {
|
|
1685
|
+
if (isAlreadyExistsConflict(err)) {
|
|
1686
|
+
if (process.env.AUI_DEBUG) {
|
|
1687
|
+
console.log(`[debug] create-tool: 409 already-exists, falling back to PATCH`);
|
|
1688
|
+
}
|
|
1689
|
+
const body = task.body;
|
|
1690
|
+
const toolCode = body.code || "";
|
|
1691
|
+
const toolName = toolCode.toUpperCase().replace(/-/g, "_");
|
|
1692
|
+
return client.patchTool(params, toolName, body);
|
|
1693
|
+
}
|
|
1694
|
+
throw err;
|
|
1695
|
+
}
|
|
1696
|
+
});
|
|
1632
1697
|
case "delete-tool":
|
|
1633
|
-
return
|
|
1698
|
+
return withTransientRetry(`DELETE tool ${task.toolName}`, async () => {
|
|
1699
|
+
try {
|
|
1700
|
+
return await client.deleteTool(params, task.toolName);
|
|
1701
|
+
}
|
|
1702
|
+
catch (err) {
|
|
1703
|
+
if (isNotFoundError(err)) {
|
|
1704
|
+
if (process.env.AUI_DEBUG) {
|
|
1705
|
+
console.log(`[debug] delete-tool ${task.toolName}: 404 already absent`);
|
|
1706
|
+
}
|
|
1707
|
+
return DELETE_ALREADY_ABSENT;
|
|
1708
|
+
}
|
|
1709
|
+
throw err;
|
|
1710
|
+
}
|
|
1711
|
+
});
|
|
1634
1712
|
case "patch-general-settings":
|
|
1635
|
-
return client.patchGeneralSettings(params, task.body);
|
|
1713
|
+
return withTransientRetry("PATCH general-settings", () => client.patchGeneralSettings(params, task.body));
|
|
1636
1714
|
case "put-parameters":
|
|
1637
|
-
return client.putParameters(params, task.body, task.oldBody);
|
|
1715
|
+
return withTransientRetry("PUT parameters", () => client.putParameters(params, task.body, task.oldBody));
|
|
1638
1716
|
case "put-entities":
|
|
1639
|
-
return client.putEntities(params, task.body, task.oldBody);
|
|
1717
|
+
return withTransientRetry("PUT entities", () => client.putEntities(params, task.body, task.oldBody));
|
|
1640
1718
|
case "put-integrations":
|
|
1641
|
-
return client.putIntegrations(params, task.body, task.oldBody);
|
|
1719
|
+
return withTransientRetry("PUT integrations", () => client.putIntegrations(params, task.body, task.oldBody));
|
|
1642
1720
|
case "create-parameter":
|
|
1643
|
-
|
|
1644
|
-
|
|
1645
|
-
|
|
1646
|
-
catch (err) {
|
|
1647
|
-
if (isAlreadyExistsConflict(err)) {
|
|
1648
|
-
return client.patchParameter(params, task.itemCode, task.body);
|
|
1721
|
+
return withTransientRetry(`POST param ${task.itemCode}`, async () => {
|
|
1722
|
+
try {
|
|
1723
|
+
return await client.createParameter(params, task.body);
|
|
1649
1724
|
}
|
|
1650
|
-
|
|
1651
|
-
|
|
1725
|
+
catch (err) {
|
|
1726
|
+
if (isAlreadyExistsConflict(err)) {
|
|
1727
|
+
if (process.env.AUI_DEBUG) {
|
|
1728
|
+
console.log(`[debug] create-parameter ${task.itemCode}: 409, falling back to PATCH`);
|
|
1729
|
+
}
|
|
1730
|
+
return client.patchParameter(params, task.itemCode, task.body);
|
|
1731
|
+
}
|
|
1732
|
+
throw err;
|
|
1733
|
+
}
|
|
1734
|
+
});
|
|
1652
1735
|
case "patch-parameter":
|
|
1653
|
-
return
|
|
1736
|
+
return withTransientRetry(`PATCH param ${task.itemCode}`, async () => {
|
|
1737
|
+
try {
|
|
1738
|
+
return await client.patchParameter(params, task.itemCode, task.body);
|
|
1739
|
+
}
|
|
1740
|
+
catch (err) {
|
|
1741
|
+
if (isNotFoundError(err)) {
|
|
1742
|
+
if (process.env.AUI_DEBUG) {
|
|
1743
|
+
console.log(`[debug] patch-parameter ${task.itemCode}: 404 not found, falling back to POST`);
|
|
1744
|
+
}
|
|
1745
|
+
return client.createParameter(params, task.body);
|
|
1746
|
+
}
|
|
1747
|
+
throw err;
|
|
1748
|
+
}
|
|
1749
|
+
});
|
|
1654
1750
|
case "delete-parameter":
|
|
1655
|
-
return
|
|
1751
|
+
return withTransientRetry(`DELETE param ${task.itemCode}`, async () => {
|
|
1752
|
+
try {
|
|
1753
|
+
return await client.deleteParameter(params, task.itemCode, task.body);
|
|
1754
|
+
}
|
|
1755
|
+
catch (err) {
|
|
1756
|
+
if (isNotFoundError(err)) {
|
|
1757
|
+
if (process.env.AUI_DEBUG) {
|
|
1758
|
+
console.log(`[debug] delete-parameter ${task.itemCode}: 404 already absent`);
|
|
1759
|
+
}
|
|
1760
|
+
return DELETE_ALREADY_ABSENT;
|
|
1761
|
+
}
|
|
1762
|
+
throw err;
|
|
1763
|
+
}
|
|
1764
|
+
});
|
|
1656
1765
|
case "create-entity":
|
|
1657
|
-
|
|
1658
|
-
|
|
1659
|
-
|
|
1660
|
-
catch (err) {
|
|
1661
|
-
if (isAlreadyExistsConflict(err)) {
|
|
1662
|
-
return client.patchEntity(params, task.itemCode, task.body);
|
|
1766
|
+
return withTransientRetry(`POST entity ${task.itemCode}`, async () => {
|
|
1767
|
+
try {
|
|
1768
|
+
return await client.createEntity(params, task.body);
|
|
1663
1769
|
}
|
|
1664
|
-
|
|
1665
|
-
|
|
1770
|
+
catch (err) {
|
|
1771
|
+
if (isAlreadyExistsConflict(err)) {
|
|
1772
|
+
if (process.env.AUI_DEBUG) {
|
|
1773
|
+
console.log(`[debug] create-entity ${task.itemCode}: 409, falling back to PATCH`);
|
|
1774
|
+
}
|
|
1775
|
+
return client.patchEntity(params, task.itemCode, task.body);
|
|
1776
|
+
}
|
|
1777
|
+
throw err;
|
|
1778
|
+
}
|
|
1779
|
+
});
|
|
1666
1780
|
case "patch-entity":
|
|
1667
|
-
return
|
|
1781
|
+
return withTransientRetry(`PATCH entity ${task.itemCode}`, async () => {
|
|
1782
|
+
try {
|
|
1783
|
+
return await client.patchEntity(params, task.itemCode, task.body);
|
|
1784
|
+
}
|
|
1785
|
+
catch (err) {
|
|
1786
|
+
if (isNotFoundError(err)) {
|
|
1787
|
+
if (process.env.AUI_DEBUG) {
|
|
1788
|
+
console.log(`[debug] patch-entity ${task.itemCode}: 404, falling back to POST`);
|
|
1789
|
+
}
|
|
1790
|
+
return client.createEntity(params, task.body);
|
|
1791
|
+
}
|
|
1792
|
+
throw err;
|
|
1793
|
+
}
|
|
1794
|
+
});
|
|
1668
1795
|
case "delete-entity":
|
|
1669
|
-
return
|
|
1796
|
+
return withTransientRetry(`DELETE entity ${task.itemCode}`, async () => {
|
|
1797
|
+
try {
|
|
1798
|
+
return await client.deleteEntity(params, task.itemCode);
|
|
1799
|
+
}
|
|
1800
|
+
catch (err) {
|
|
1801
|
+
if (isNotFoundError(err)) {
|
|
1802
|
+
if (process.env.AUI_DEBUG) {
|
|
1803
|
+
console.log(`[debug] delete-entity ${task.itemCode}: 404 already absent`);
|
|
1804
|
+
}
|
|
1805
|
+
return DELETE_ALREADY_ABSENT;
|
|
1806
|
+
}
|
|
1807
|
+
throw err;
|
|
1808
|
+
}
|
|
1809
|
+
});
|
|
1670
1810
|
case "create-integration":
|
|
1671
|
-
|
|
1672
|
-
|
|
1673
|
-
|
|
1674
|
-
catch (err) {
|
|
1675
|
-
if (isAlreadyExistsConflict(err)) {
|
|
1676
|
-
return client.patchIntegration(params, task.itemCode, task.body);
|
|
1811
|
+
return withTransientRetry(`POST integration ${task.itemCode}`, async () => {
|
|
1812
|
+
try {
|
|
1813
|
+
return await client.createIntegration(params, task.body);
|
|
1677
1814
|
}
|
|
1678
|
-
|
|
1679
|
-
|
|
1815
|
+
catch (err) {
|
|
1816
|
+
if (isAlreadyExistsConflict(err)) {
|
|
1817
|
+
if (process.env.AUI_DEBUG) {
|
|
1818
|
+
console.log(`[debug] create-integration ${task.itemCode}: 409, falling back to PATCH`);
|
|
1819
|
+
}
|
|
1820
|
+
return client.patchIntegration(params, task.itemCode, task.body);
|
|
1821
|
+
}
|
|
1822
|
+
throw err;
|
|
1823
|
+
}
|
|
1824
|
+
});
|
|
1680
1825
|
case "patch-integration":
|
|
1681
|
-
return
|
|
1826
|
+
return withTransientRetry(`PATCH integration ${task.itemCode}`, async () => {
|
|
1827
|
+
try {
|
|
1828
|
+
return await client.patchIntegration(params, task.itemCode, task.body);
|
|
1829
|
+
}
|
|
1830
|
+
catch (err) {
|
|
1831
|
+
if (isNotFoundError(err)) {
|
|
1832
|
+
if (process.env.AUI_DEBUG) {
|
|
1833
|
+
console.log(`[debug] patch-integration ${task.itemCode}: 404 not found, falling back to POST`);
|
|
1834
|
+
}
|
|
1835
|
+
return client.createIntegration(params, task.body);
|
|
1836
|
+
}
|
|
1837
|
+
throw err;
|
|
1838
|
+
}
|
|
1839
|
+
});
|
|
1682
1840
|
case "delete-integration":
|
|
1683
|
-
return
|
|
1841
|
+
return withTransientRetry(`DELETE integration ${task.itemCode}`, async () => {
|
|
1842
|
+
try {
|
|
1843
|
+
return await client.deleteIntegration(params, task.itemCode);
|
|
1844
|
+
}
|
|
1845
|
+
catch (err) {
|
|
1846
|
+
if (isNotFoundError(err)) {
|
|
1847
|
+
if (process.env.AUI_DEBUG) {
|
|
1848
|
+
console.log(`[debug] delete-integration ${task.itemCode}: 404 already absent`);
|
|
1849
|
+
}
|
|
1850
|
+
return DELETE_ALREADY_ABSENT;
|
|
1851
|
+
}
|
|
1852
|
+
throw err;
|
|
1853
|
+
}
|
|
1854
|
+
});
|
|
1684
1855
|
case "put-rules":
|
|
1685
|
-
return client.putRules(params, task.body);
|
|
1856
|
+
return withTransientRetry("PUT rules", () => client.putRules(params, task.body));
|
|
1686
1857
|
default:
|
|
1687
1858
|
throw new Error(`Unknown push task type: ${task.type}`);
|
|
1688
1859
|
}
|