@refrainai/cli 0.4.1 → 0.4.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/{ai-model-FM6GWCID.js → ai-model-DP5PKGM6.js} +2 -2
- package/dist/{chunk-2BVDAJZT.js → chunk-65CTEK2K.js} +9 -6
- package/dist/chunk-65CTEK2K.js.map +1 -0
- package/dist/chunk-BGC75OVR.js +30 -0
- package/dist/chunk-BGC75OVR.js.map +1 -0
- package/dist/{chunk-H47NWH7N.js → chunk-CJM3IU5Q.js} +611 -73
- package/dist/chunk-CJM3IU5Q.js.map +1 -0
- package/dist/{chunk-CLYJHKPY.js → chunk-CMWLFQXD.js} +43 -42
- package/dist/chunk-CMWLFQXD.js.map +1 -0
- package/dist/{chunk-IGFCYKHC.js → chunk-CNJ5KCDN.js} +252 -295
- package/dist/chunk-CNJ5KCDN.js.map +1 -0
- package/dist/{chunk-DJVUITRB.js → chunk-ELQ23L4Z.js} +898 -1135
- package/dist/chunk-ELQ23L4Z.js.map +1 -0
- package/dist/chunk-EMAYENG4.js +1146 -0
- package/dist/chunk-EMAYENG4.js.map +1 -0
- package/dist/{chunk-7UCVPKD4.js → chunk-F7WTOQIQ.js} +12 -72
- package/dist/chunk-F7WTOQIQ.js.map +1 -0
- package/dist/{chunk-WEYR56ZN.js → chunk-HHRHHFSK.js} +4 -4
- package/dist/{chunk-UGPXCQY3.js → chunk-KFNW4XR2.js} +13 -4
- package/dist/chunk-KFNW4XR2.js.map +1 -0
- package/dist/{chunk-RT664YIO.js → chunk-LZDZGI4M.js} +3 -1
- package/dist/chunk-LZDZGI4M.js.map +1 -0
- package/dist/chunk-RBZK7T76.js +349 -0
- package/dist/chunk-RBZK7T76.js.map +1 -0
- package/dist/{chunk-HQDXLWAY.js → chunk-SDV3X5UN.js} +2 -2
- package/dist/{chunk-Z33FCOTZ.js → chunk-VVXNFUPL.js} +4 -2
- package/dist/chunk-VVXNFUPL.js.map +1 -0
- package/dist/chunk-XIVS7N3V.js +74 -0
- package/dist/chunk-XIVS7N3V.js.map +1 -0
- package/dist/chunk-YTVEYQGA.js +64 -0
- package/dist/chunk-YTVEYQGA.js.map +1 -0
- package/dist/{chunk-RYIJPYM3.js → chunk-YW46VP57.js} +25 -8
- package/dist/chunk-YW46VP57.js.map +1 -0
- package/dist/cli.js +5 -5
- package/dist/{compose-MTSIJY5D.js → compose-B2IAO7YW.js} +9 -7
- package/dist/{compose-MTSIJY5D.js.map → compose-B2IAO7YW.js.map} +1 -1
- package/dist/extraction-prompt-VDCKIFLB.js +17 -0
- package/dist/extraction-prompt-VDCKIFLB.js.map +1 -0
- package/dist/{fix-runbook-ZSBOTLC2.js → fix-runbook-JJN4HVIP.js} +12 -10
- package/dist/{fix-runbook-ZSBOTLC2.js.map → fix-runbook-JJN4HVIP.js.map} +1 -1
- package/dist/prompts-XMJXIITW.js +13 -0
- package/dist/runbook-builder-2ZLE2AEO.js +11 -0
- package/dist/{runbook-data-helpers-KRR2SH76.js → runbook-data-helpers-5UAO65TZ.js} +3 -3
- package/dist/{runbook-executor-K7T6RJWJ.js → runbook-executor-TVV5EG6Q.js} +41 -444
- package/dist/runbook-executor-TVV5EG6Q.js.map +1 -0
- package/dist/{runbook-generator-MPXJBQ5N.js → runbook-generator-4XKNV2B7.js} +61 -136
- package/dist/runbook-generator-4XKNV2B7.js.map +1 -0
- package/dist/{runbook-schema-3T6TP3JJ.js → runbook-schema-X7DW725O.js} +2 -2
- package/dist/runbook-store-S24PXIHD.js +11 -0
- package/dist/{schema-5G6UQSPT.js → schema-XFSD5EWN.js} +2 -2
- package/dist/{server-AG3LXQBI.js → server-5XARL5N7.js} +1176 -128
- package/dist/server-5XARL5N7.js.map +1 -0
- package/dist/{tenant-ai-config-QPFEJUVJ.js → tenant-ai-config-4NHKRW7O.js} +4 -4
- package/dist/tenant-ai-config-4NHKRW7O.js.map +1 -0
- package/dist/yaml-patcher-32QBPXT2.js +18 -0
- package/dist/yaml-patcher-32QBPXT2.js.map +1 -0
- package/package.json +3 -2
- package/dist/chunk-2BVDAJZT.js.map +0 -1
- package/dist/chunk-7UCVPKD4.js.map +0 -1
- package/dist/chunk-CLYJHKPY.js.map +0 -1
- package/dist/chunk-DJVUITRB.js.map +0 -1
- package/dist/chunk-H47NWH7N.js.map +0 -1
- package/dist/chunk-IGFCYKHC.js.map +0 -1
- package/dist/chunk-RT664YIO.js.map +0 -1
- package/dist/chunk-RYIJPYM3.js.map +0 -1
- package/dist/chunk-UGPXCQY3.js.map +0 -1
- package/dist/chunk-VPK2MQAZ.js +0 -589
- package/dist/chunk-VPK2MQAZ.js.map +0 -1
- package/dist/chunk-Z33FCOTZ.js.map +0 -1
- package/dist/runbook-executor-K7T6RJWJ.js.map +0 -1
- package/dist/runbook-generator-MPXJBQ5N.js.map +0 -1
- package/dist/runbook-store-G5GUOWRR.js +0 -11
- package/dist/server-AG3LXQBI.js.map +0 -1
- package/dist/yaml-patcher-VGUS2JGH.js +0 -15
- /package/dist/{ai-model-FM6GWCID.js.map → ai-model-DP5PKGM6.js.map} +0 -0
- /package/dist/{chunk-WEYR56ZN.js.map → chunk-HHRHHFSK.js.map} +0 -0
- /package/dist/{chunk-HQDXLWAY.js.map → chunk-SDV3X5UN.js.map} +0 -0
- /package/dist/{runbook-data-helpers-KRR2SH76.js.map → prompts-XMJXIITW.js.map} +0 -0
- /package/dist/{runbook-schema-3T6TP3JJ.js.map → runbook-builder-2ZLE2AEO.js.map} +0 -0
- /package/dist/{runbook-store-G5GUOWRR.js.map → runbook-data-helpers-5UAO65TZ.js.map} +0 -0
- /package/dist/{schema-5G6UQSPT.js.map → runbook-schema-X7DW725O.js.map} +0 -0
- /package/dist/{tenant-ai-config-QPFEJUVJ.js.map → runbook-store-S24PXIHD.js.map} +0 -0
- /package/dist/{yaml-patcher-VGUS2JGH.js.map → schema-XFSD5EWN.js.map} +0 -0
|
@@ -1,4 +1,56 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
|
+
import {
|
|
3
|
+
RunbookStore
|
|
4
|
+
} from "./chunk-HHRHHFSK.js";
|
|
5
|
+
import {
|
|
6
|
+
isEditable,
|
|
7
|
+
isVerifiable
|
|
8
|
+
} from "./chunk-VVXNFUPL.js";
|
|
9
|
+
import {
|
|
10
|
+
explore,
|
|
11
|
+
generateGenerationReportFile
|
|
12
|
+
} from "./chunk-CNJ5KCDN.js";
|
|
13
|
+
import {
|
|
14
|
+
COMMUNITY_PLAN,
|
|
15
|
+
DEFAULT_APPROVAL_TIMEOUT_MS,
|
|
16
|
+
DEFAULT_CALLBACK_PORT,
|
|
17
|
+
PLAN_PRICING,
|
|
18
|
+
RuntimeStore,
|
|
19
|
+
SELF_HEAL_DEFAULTS,
|
|
20
|
+
SharedChatBot,
|
|
21
|
+
TIER_TO_PLAN,
|
|
22
|
+
analyzeDebugResult,
|
|
23
|
+
applyExecutorDefaults,
|
|
24
|
+
buildApprovalCard,
|
|
25
|
+
buildApprovalResultCard,
|
|
26
|
+
buildDebugNotificationCard,
|
|
27
|
+
buildGenerateNotificationCard,
|
|
28
|
+
buildNotificationCard,
|
|
29
|
+
buildSelectorHint,
|
|
30
|
+
detectKeyKind,
|
|
31
|
+
enforceFeatureGates,
|
|
32
|
+
execute,
|
|
33
|
+
generateDebugToken,
|
|
34
|
+
generateExecutionReportFile,
|
|
35
|
+
getDebugPublicKey,
|
|
36
|
+
getDebugSigningKey,
|
|
37
|
+
mergeVariablesAndSecrets,
|
|
38
|
+
normalizeUrlPattern,
|
|
39
|
+
resolvePlanFromTier,
|
|
40
|
+
resolveVariablesCore
|
|
41
|
+
} from "./chunk-CJM3IU5Q.js";
|
|
42
|
+
import {
|
|
43
|
+
AgentBrowser,
|
|
44
|
+
DownloadManager,
|
|
45
|
+
InMemoryDataStore,
|
|
46
|
+
deserializeSecrets,
|
|
47
|
+
mergeVariablesIntoSecrets,
|
|
48
|
+
serializeSecrets
|
|
49
|
+
} from "./chunk-ELQ23L4Z.js";
|
|
50
|
+
import {
|
|
51
|
+
scanStepsForTemplates
|
|
52
|
+
} from "./chunk-D5SI2PHK.js";
|
|
53
|
+
import "./chunk-XIVS7N3V.js";
|
|
2
54
|
import {
|
|
3
55
|
loadServerConfig
|
|
4
56
|
} from "./chunk-TDSM3UXI.js";
|
|
@@ -8,14 +60,7 @@ import {
|
|
|
8
60
|
isMaskedValue,
|
|
9
61
|
maskSensitiveValue,
|
|
10
62
|
resolveTenantAIConfig
|
|
11
|
-
} from "./chunk-
|
|
12
|
-
import {
|
|
13
|
-
RunbookStore
|
|
14
|
-
} from "./chunk-WEYR56ZN.js";
|
|
15
|
-
import {
|
|
16
|
-
isEditable,
|
|
17
|
-
isVerifiable
|
|
18
|
-
} from "./chunk-Z33FCOTZ.js";
|
|
63
|
+
} from "./chunk-SDV3X5UN.js";
|
|
19
64
|
import {
|
|
20
65
|
apiKeys,
|
|
21
66
|
invitations,
|
|
@@ -37,6 +82,7 @@ import {
|
|
|
37
82
|
runbookSettings,
|
|
38
83
|
runbookSteps,
|
|
39
84
|
runbookVariables,
|
|
85
|
+
runbookVersionReviews,
|
|
40
86
|
runbookVersions,
|
|
41
87
|
runbooks,
|
|
42
88
|
schedules,
|
|
@@ -53,60 +99,7 @@ import {
|
|
|
53
99
|
tenantUsage,
|
|
54
100
|
tenants,
|
|
55
101
|
user
|
|
56
|
-
} from "./chunk-
|
|
57
|
-
import {
|
|
58
|
-
buildRunbookYaml,
|
|
59
|
-
createReviewPrompt,
|
|
60
|
-
explore,
|
|
61
|
-
reviewResponseSchema
|
|
62
|
-
} from "./chunk-IGFCYKHC.js";
|
|
63
|
-
import {
|
|
64
|
-
COMMUNITY_PLAN,
|
|
65
|
-
DEFAULT_APPROVAL_TIMEOUT_MS,
|
|
66
|
-
DEFAULT_CALLBACK_PORT,
|
|
67
|
-
PLAN_PRICING,
|
|
68
|
-
RuntimeStore,
|
|
69
|
-
SELF_HEAL_DEFAULTS,
|
|
70
|
-
SharedChatBot,
|
|
71
|
-
TIER_TO_PLAN,
|
|
72
|
-
analyzeDebugResult,
|
|
73
|
-
applyExecutorDefaults,
|
|
74
|
-
buildApprovalCard,
|
|
75
|
-
buildApprovalResultCard,
|
|
76
|
-
buildDebugNotificationCard,
|
|
77
|
-
buildGenerateNotificationCard,
|
|
78
|
-
buildNotificationCard,
|
|
79
|
-
buildSelectorHint,
|
|
80
|
-
detectKeyKind,
|
|
81
|
-
enforceFeatureGates,
|
|
82
|
-
execute,
|
|
83
|
-
generateDebugToken,
|
|
84
|
-
getDebugPublicKey,
|
|
85
|
-
getDebugSigningKey,
|
|
86
|
-
mergeVariablesAndSecrets,
|
|
87
|
-
normalizeUrlPattern,
|
|
88
|
-
resolvePlanFromTier,
|
|
89
|
-
resolveVariablesCore
|
|
90
|
-
} from "./chunk-H47NWH7N.js";
|
|
91
|
-
import {
|
|
92
|
-
AgentBrowser,
|
|
93
|
-
DownloadManager,
|
|
94
|
-
InMemoryDataStore,
|
|
95
|
-
deserializeSecrets,
|
|
96
|
-
mergeVariablesIntoSecrets,
|
|
97
|
-
serializeSecrets
|
|
98
|
-
} from "./chunk-DJVUITRB.js";
|
|
99
|
-
import "./chunk-XMFCXPYU.js";
|
|
100
|
-
import "./chunk-AG3CFMYU.js";
|
|
101
|
-
import {
|
|
102
|
-
scanStepsForTemplates
|
|
103
|
-
} from "./chunk-D5SI2PHK.js";
|
|
104
|
-
import {
|
|
105
|
-
resolveAcceptLanguage,
|
|
106
|
-
t,
|
|
107
|
-
tWithLocale,
|
|
108
|
-
tfWithLocale
|
|
109
|
-
} from "./chunk-7UCVPKD4.js";
|
|
102
|
+
} from "./chunk-CMWLFQXD.js";
|
|
110
103
|
import {
|
|
111
104
|
AIMetricsCollector,
|
|
112
105
|
DEFAULT_MODEL_ID,
|
|
@@ -115,10 +108,27 @@ import {
|
|
|
115
108
|
getUserFriendlyAIErrorFromMessage,
|
|
116
109
|
runWithMetricsCollector,
|
|
117
110
|
trackedGenerateObject
|
|
118
|
-
} from "./chunk-
|
|
111
|
+
} from "./chunk-KFNW4XR2.js";
|
|
112
|
+
import "./chunk-XMFCXPYU.js";
|
|
113
|
+
import "./chunk-AG3CFMYU.js";
|
|
114
|
+
import {
|
|
115
|
+
buildRunbookYaml
|
|
116
|
+
} from "./chunk-RBZK7T76.js";
|
|
117
|
+
import "./chunk-YTVEYQGA.js";
|
|
119
118
|
import {
|
|
120
119
|
ParsedRunbookSchema
|
|
121
|
-
} from "./chunk-
|
|
120
|
+
} from "./chunk-LZDZGI4M.js";
|
|
121
|
+
import {
|
|
122
|
+
createReviewPrompt,
|
|
123
|
+
reviewResponseSchema
|
|
124
|
+
} from "./chunk-BGC75OVR.js";
|
|
125
|
+
import "./chunk-EMAYENG4.js";
|
|
126
|
+
import {
|
|
127
|
+
resolveAcceptLanguage,
|
|
128
|
+
t,
|
|
129
|
+
tWithLocale,
|
|
130
|
+
tfWithLocale
|
|
131
|
+
} from "./chunk-F7WTOQIQ.js";
|
|
122
132
|
import "./chunk-2H7UOFLK.js";
|
|
123
133
|
|
|
124
134
|
// src/server/index.ts
|
|
@@ -126,7 +136,7 @@ import { execSync } from "child_process";
|
|
|
126
136
|
import { serve } from "@hono/node-server";
|
|
127
137
|
|
|
128
138
|
// src/server/app.ts
|
|
129
|
-
import { Hono as
|
|
139
|
+
import { Hono as Hono16 } from "hono";
|
|
130
140
|
import { cors } from "hono/cors";
|
|
131
141
|
import { logger } from "hono/logger";
|
|
132
142
|
import { serve as serveInngest } from "inngest/hono";
|
|
@@ -484,7 +494,7 @@ async function closeDb() {
|
|
|
484
494
|
}
|
|
485
495
|
|
|
486
496
|
// src/server/store/job-store.ts
|
|
487
|
-
import { eq as eq2, and, desc, count, sql } from "drizzle-orm";
|
|
497
|
+
import { eq as eq2, and, desc, count, sql, inArray } from "drizzle-orm";
|
|
488
498
|
var JobStore = class {
|
|
489
499
|
constructor(db) {
|
|
490
500
|
this.db = db;
|
|
@@ -634,10 +644,14 @@ var JobStore = class {
|
|
|
634
644
|
};
|
|
635
645
|
}
|
|
636
646
|
async list(tenantId, opts = {}) {
|
|
637
|
-
const { runbookId, status, mode, limit = 50, offset = 0 } = opts;
|
|
647
|
+
const { runbookId, status, statuses, mode, limit = 50, offset = 0 } = opts;
|
|
638
648
|
const conditions = [eq2(jobs.tenantId, tenantId)];
|
|
639
649
|
if (runbookId) conditions.push(eq2(jobs.runbookId, runbookId));
|
|
640
|
-
if (
|
|
650
|
+
if (statuses && statuses.length > 0) {
|
|
651
|
+
conditions.push(inArray(jobs.status, statuses));
|
|
652
|
+
} else if (status) {
|
|
653
|
+
conditions.push(eq2(jobs.status, status));
|
|
654
|
+
}
|
|
641
655
|
if (mode) conditions.push(eq2(jobs.mode, mode));
|
|
642
656
|
const where = and(...conditions);
|
|
643
657
|
const rows = await this.db.select().from(jobs).where(where).orderBy(desc(jobs.createdAt)).limit(limit).offset(offset);
|
|
@@ -889,7 +903,7 @@ var JobStore = class {
|
|
|
889
903
|
};
|
|
890
904
|
|
|
891
905
|
// src/server/store/artifact-store.ts
|
|
892
|
-
import { eq as eq3, and as and2, asc, inArray } from "drizzle-orm";
|
|
906
|
+
import { eq as eq3, and as and2, asc, inArray as inArray2 } from "drizzle-orm";
|
|
893
907
|
var ArtifactDBStore = class {
|
|
894
908
|
constructor(db) {
|
|
895
909
|
this.db = db;
|
|
@@ -943,7 +957,7 @@ var ArtifactDBStore = class {
|
|
|
943
957
|
const rows = await this.db.selectDistinct({ jobId: jobArtifacts.jobId }).from(jobArtifacts).where(
|
|
944
958
|
and2(
|
|
945
959
|
eq3(jobArtifacts.tenantId, tenantId),
|
|
946
|
-
|
|
960
|
+
inArray2(jobArtifacts.jobId, jobIds)
|
|
947
961
|
)
|
|
948
962
|
);
|
|
949
963
|
return new Set(rows.map((r) => r.jobId));
|
|
@@ -968,7 +982,7 @@ var ArtifactDBStore = class {
|
|
|
968
982
|
const toDelete = all.slice(0, excess);
|
|
969
983
|
const ids = toDelete.map((r) => r.id);
|
|
970
984
|
const paths = toDelete.map((r) => r.storagePath);
|
|
971
|
-
await this.db.delete(jobArtifacts).where(
|
|
985
|
+
await this.db.delete(jobArtifacts).where(inArray2(jobArtifacts.id, ids));
|
|
972
986
|
return { purgedCount: ids.length, purgedPaths: paths };
|
|
973
987
|
}
|
|
974
988
|
};
|
|
@@ -1030,6 +1044,7 @@ async function loadRunbookForExecution(db, tenantId, runbookId, encryptionKey, v
|
|
|
1030
1044
|
if (dbStep.value) action.value = dbStep.value;
|
|
1031
1045
|
if (dbStep.optionText) action.optionText = dbStep.optionText;
|
|
1032
1046
|
if (dbStep.script) action.script = dbStep.script;
|
|
1047
|
+
if (dbStep.extractPrompt) action.extractPrompt = dbStep.extractPrompt;
|
|
1033
1048
|
if (dbStep.keys) action.keys = dbStep.keys;
|
|
1034
1049
|
if (dbStep.downloadPath) action.downloadPath = dbStep.downloadPath;
|
|
1035
1050
|
if (dbStep.exportCollection) action.exportCollection = dbStep.exportCollection;
|
|
@@ -2402,6 +2417,49 @@ var TelemetryStore = class {
|
|
|
2402
2417
|
suggestionOutcomes
|
|
2403
2418
|
};
|
|
2404
2419
|
}
|
|
2420
|
+
/**
|
|
2421
|
+
* ジョブレポート生成に必要な全データを一括取得する。
|
|
2422
|
+
* モードに応じて必要なテーブルを並列取得。
|
|
2423
|
+
*/
|
|
2424
|
+
async getJobReportData(jobId, mode) {
|
|
2425
|
+
const aiMetricsPromise = this.db.select().from(jobAiMetrics).where(eq7(jobAiMetrics.jobId, jobId)).limit(1);
|
|
2426
|
+
if (mode === "execute" || mode === "self_heal") {
|
|
2427
|
+
const [
|
|
2428
|
+
execDetails,
|
|
2429
|
+
[executionConfig],
|
|
2430
|
+
[telemetrySummary],
|
|
2431
|
+
resolutionOutcomes,
|
|
2432
|
+
failureEvents,
|
|
2433
|
+
[aiMetricsRow2],
|
|
2434
|
+
...selfHealData
|
|
2435
|
+
] = await Promise.all([
|
|
2436
|
+
this.getJobExecutionDetails(jobId),
|
|
2437
|
+
this.db.select().from(jobExecutionConfigs).where(eq7(jobExecutionConfigs.jobId, jobId)).limit(1),
|
|
2438
|
+
this.db.select().from(telemetryJobSummaries).where(eq7(telemetryJobSummaries.jobId, jobId)).limit(1),
|
|
2439
|
+
this.db.select().from(telemetryResolutionOutcomes).where(eq7(telemetryResolutionOutcomes.jobId, jobId)).orderBy(telemetryResolutionOutcomes.stepOrdinal),
|
|
2440
|
+
this.db.select().from(telemetryFailureEvents).where(eq7(telemetryFailureEvents.jobId, jobId)).orderBy(telemetryFailureEvents.stepOrdinal),
|
|
2441
|
+
aiMetricsPromise,
|
|
2442
|
+
...mode === "self_heal" ? [
|
|
2443
|
+
this.db.select().from(jobDebugSuggestions).where(eq7(jobDebugSuggestions.jobId, jobId)),
|
|
2444
|
+
this.db.select().from(jobDebugWarnings).where(eq7(jobDebugWarnings.jobId, jobId))
|
|
2445
|
+
] : []
|
|
2446
|
+
]);
|
|
2447
|
+
return {
|
|
2448
|
+
aiMetrics: aiMetricsRow2 ?? null,
|
|
2449
|
+
stepResults: execDetails.stepResults,
|
|
2450
|
+
executionConfig: executionConfig ?? null,
|
|
2451
|
+
telemetrySummary: telemetrySummary ?? null,
|
|
2452
|
+
resolutionOutcomes,
|
|
2453
|
+
failureEvents,
|
|
2454
|
+
debugSuggestions: mode === "self_heal" ? selfHealData[0] ?? [] : void 0,
|
|
2455
|
+
debugWarnings: mode === "self_heal" ? selfHealData[1] ?? [] : void 0
|
|
2456
|
+
};
|
|
2457
|
+
}
|
|
2458
|
+
const [aiMetricsRow] = await aiMetricsPromise;
|
|
2459
|
+
return {
|
|
2460
|
+
aiMetrics: aiMetricsRow ?? null
|
|
2461
|
+
};
|
|
2462
|
+
}
|
|
2405
2463
|
/** ジョブ実行詳細(jobResults + diagnostics + aiMetrics)を取得 */
|
|
2406
2464
|
async getJobExecutionDetails(jobId) {
|
|
2407
2465
|
const results = await this.db.select().from(jobResults).where(eq7(jobResults.jobId, jobId)).orderBy(jobResults.ordinal);
|
|
@@ -3399,6 +3457,7 @@ var generateJob = inngest.createFunction(
|
|
|
3399
3457
|
stealth: gc?.stealth,
|
|
3400
3458
|
proxy: gc?.proxy,
|
|
3401
3459
|
skills: gc?.skills ?? resolvedConfig.skills,
|
|
3460
|
+
forceReport: false,
|
|
3402
3461
|
aiModelConfig: resolvedConfig.aiConfig
|
|
3403
3462
|
};
|
|
3404
3463
|
const browser = new AgentBrowser();
|
|
@@ -3431,7 +3490,12 @@ var generateJob = inngest.createFunction(
|
|
|
3431
3490
|
onExplorerStep: async (stepNumber, description, recordedStep) => {
|
|
3432
3491
|
await emitter.emit({
|
|
3433
3492
|
type: "explorer_step",
|
|
3434
|
-
data: {
|
|
3493
|
+
data: {
|
|
3494
|
+
stepNumber,
|
|
3495
|
+
ordinal: recordedStep.ordinal,
|
|
3496
|
+
description,
|
|
3497
|
+
actionType: recordedStep.action.action
|
|
3498
|
+
}
|
|
3435
3499
|
});
|
|
3436
3500
|
await jobStore.updateStatus(jobId, "exploring", { currentStep: stepNumber });
|
|
3437
3501
|
await jobStore.saveRecordedSteps(jobId, [{
|
|
@@ -3551,9 +3615,17 @@ var generateJob = inngest.createFunction(
|
|
|
3551
3615
|
});
|
|
3552
3616
|
let yamlGenerated = false;
|
|
3553
3617
|
await step.run("save-output", async () => {
|
|
3618
|
+
let existingTitle;
|
|
3619
|
+
if (jobInfo.runbookId) {
|
|
3620
|
+
const { RunbookStore: RS } = await import("./runbook-store-S24PXIHD.js");
|
|
3621
|
+
const rs = new RS(db);
|
|
3622
|
+
const existingRb = await rs.findById(tenantId, jobInfo.runbookId);
|
|
3623
|
+
existingTitle = existingRb?.title;
|
|
3624
|
+
}
|
|
3554
3625
|
let yamlContent;
|
|
3555
3626
|
try {
|
|
3556
3627
|
yamlContent = buildRunbookYaml({
|
|
3628
|
+
title: existingTitle,
|
|
3557
3629
|
goal: jobInfo.goal,
|
|
3558
3630
|
startUrl: jobInfo.startUrl,
|
|
3559
3631
|
recordedSteps: exploreResult.recordedSteps,
|
|
@@ -3582,8 +3654,8 @@ var generateJob = inngest.createFunction(
|
|
|
3582
3654
|
});
|
|
3583
3655
|
if (jobInfo.runbookId && yamlContent) {
|
|
3584
3656
|
const { parse: yamlParse } = await import("yaml");
|
|
3585
|
-
const { ParsedRunbookSchema: ParsedRunbookSchema2 } = await import("./runbook-schema-
|
|
3586
|
-
const { RunbookStore: RunbookStore2 } = await import("./runbook-store-
|
|
3657
|
+
const { ParsedRunbookSchema: ParsedRunbookSchema2 } = await import("./runbook-schema-X7DW725O.js");
|
|
3658
|
+
const { RunbookStore: RunbookStore2 } = await import("./runbook-store-S24PXIHD.js");
|
|
3587
3659
|
const parsed = ParsedRunbookSchema2.safeParse(yamlParse(yamlContent));
|
|
3588
3660
|
if (parsed.success) {
|
|
3589
3661
|
const runbookStore = new RunbookStore2(db);
|
|
@@ -3821,9 +3893,9 @@ var schedulePoller = inngest.createFunction(
|
|
|
3821
3893
|
continue;
|
|
3822
3894
|
}
|
|
3823
3895
|
const activeVersionId = rb.activeVersionId;
|
|
3824
|
-
const { runbookSteps: runbookSteps2 } = await import("./schema-
|
|
3825
|
-
const { count:
|
|
3826
|
-
const [stepCountResult] = await db.select({ cnt:
|
|
3896
|
+
const { runbookSteps: runbookSteps2 } = await import("./schema-XFSD5EWN.js");
|
|
3897
|
+
const { count: count7, isNull: isNull4 } = await import("drizzle-orm");
|
|
3898
|
+
const [stepCountResult] = await db.select({ cnt: count7() }).from(runbookSteps2).where(
|
|
3827
3899
|
and6(
|
|
3828
3900
|
eq11(runbookSteps2.versionId, activeVersionId),
|
|
3829
3901
|
isNull4(runbookSteps2.parentStepId)
|
|
@@ -4375,6 +4447,23 @@ function createRunbookHandlers(store, jobStore, inngest2, encryptionKey, schedul
|
|
|
4375
4447
|
return c.json({ error: "Not found" }, 404);
|
|
4376
4448
|
}
|
|
4377
4449
|
const totalSteps = rb.steps.filter((s) => !s.parentStepId).length;
|
|
4450
|
+
const body = await c.req.json().catch(() => ({}));
|
|
4451
|
+
const storedVars = rb.variables.filter((v) => v.value != null).map((v) => ({
|
|
4452
|
+
key: v.name,
|
|
4453
|
+
value: v.value,
|
|
4454
|
+
sensitive: v.sensitive
|
|
4455
|
+
}));
|
|
4456
|
+
if (body.variables) {
|
|
4457
|
+
for (const [key, value] of Object.entries(body.variables)) {
|
|
4458
|
+
const existing = storedVars.find((v) => v.key === key);
|
|
4459
|
+
const varDef = rb.variables.find((v) => v.name === key);
|
|
4460
|
+
if (existing) {
|
|
4461
|
+
existing.value = value;
|
|
4462
|
+
} else {
|
|
4463
|
+
storedVars.push({ key, value, sensitive: varDef?.sensitive ?? false });
|
|
4464
|
+
}
|
|
4465
|
+
}
|
|
4466
|
+
}
|
|
4378
4467
|
if (totalSteps === 0) {
|
|
4379
4468
|
if (!rb.startUrl || !rb.goal) {
|
|
4380
4469
|
return c.json({ error: "startUrl and goal are required for generation" }, 400);
|
|
@@ -4388,7 +4477,8 @@ function createRunbookHandlers(store, jobStore, inngest2, encryptionKey, schedul
|
|
|
4388
4477
|
createdBy: userId,
|
|
4389
4478
|
startUrl: rb.startUrl,
|
|
4390
4479
|
goal: rb.goal,
|
|
4391
|
-
context: rb.context ?? ""
|
|
4480
|
+
context: rb.context ?? "",
|
|
4481
|
+
variables: storedVars.length > 0 ? storedVars : void 0
|
|
4392
4482
|
});
|
|
4393
4483
|
await store.setVerificationJob(tenantId, id, job2.id);
|
|
4394
4484
|
await inngest2.send({
|
|
@@ -4404,6 +4494,7 @@ function createRunbookHandlers(store, jobStore, inngest2, encryptionKey, schedul
|
|
|
4404
4494
|
totalSteps,
|
|
4405
4495
|
purpose: "verification",
|
|
4406
4496
|
createdBy: userId,
|
|
4497
|
+
variables: storedVars.length > 0 ? storedVars : void 0,
|
|
4407
4498
|
executionConfig: buildExecutionConfig(rb.settings)
|
|
4408
4499
|
});
|
|
4409
4500
|
await store.setVerificationJob(tenantId, id, job.id);
|
|
@@ -4454,6 +4545,11 @@ function createRunbookHandlers(store, jobStore, inngest2, encryptionKey, schedul
|
|
|
4454
4545
|
if (db && rb.currentVersionId) {
|
|
4455
4546
|
await db.delete(runbookSteps).where(eq13(runbookSteps.versionId, rb.currentVersionId));
|
|
4456
4547
|
}
|
|
4548
|
+
const regenStoredVars = rb.variables.filter((v) => v.value != null).map((v) => ({
|
|
4549
|
+
key: v.name,
|
|
4550
|
+
value: v.value,
|
|
4551
|
+
sensitive: v.sensitive
|
|
4552
|
+
}));
|
|
4457
4553
|
const job = await jobStore.insert({
|
|
4458
4554
|
tenantId,
|
|
4459
4555
|
mode: "generate",
|
|
@@ -4463,7 +4559,8 @@ function createRunbookHandlers(store, jobStore, inngest2, encryptionKey, schedul
|
|
|
4463
4559
|
createdBy: userId,
|
|
4464
4560
|
startUrl: rb.startUrl,
|
|
4465
4561
|
goal: rb.goal,
|
|
4466
|
-
context: rb.context ?? void 0
|
|
4562
|
+
context: rb.context ?? void 0,
|
|
4563
|
+
variables: regenStoredVars.length > 0 ? regenStoredVars : void 0
|
|
4467
4564
|
});
|
|
4468
4565
|
await store.setVerificationJob(tenantId, id, job.id);
|
|
4469
4566
|
await inngest2.send({
|
|
@@ -4773,6 +4870,23 @@ function createRunbookHandlers(store, jobStore, inngest2, encryptionKey, schedul
|
|
|
4773
4870
|
return c.json({ error: `Cannot verify: version is ${version.status}` }, 400);
|
|
4774
4871
|
}
|
|
4775
4872
|
const totalSteps = version.steps.filter((s) => !s.parentStepId).length;
|
|
4873
|
+
const vBody = await c.req.json().catch(() => ({}));
|
|
4874
|
+
const vStoredVars = version.variables.filter((v) => v.value != null).map((v) => ({
|
|
4875
|
+
key: v.name,
|
|
4876
|
+
value: v.value,
|
|
4877
|
+
sensitive: v.sensitive
|
|
4878
|
+
}));
|
|
4879
|
+
if (vBody.variables) {
|
|
4880
|
+
for (const [key, value] of Object.entries(vBody.variables)) {
|
|
4881
|
+
const existing = vStoredVars.find((v) => v.key === key);
|
|
4882
|
+
const varDef = version.variables.find((v) => v.name === key);
|
|
4883
|
+
if (existing) {
|
|
4884
|
+
existing.value = value;
|
|
4885
|
+
} else {
|
|
4886
|
+
vStoredVars.push({ key, value, sensitive: varDef?.sensitive ?? false });
|
|
4887
|
+
}
|
|
4888
|
+
}
|
|
4889
|
+
}
|
|
4776
4890
|
if (totalSteps === 0) {
|
|
4777
4891
|
if (!version.startUrl || !version.goal) {
|
|
4778
4892
|
return c.json({ error: "startUrl and goal are required for generation" }, 400);
|
|
@@ -4786,7 +4900,8 @@ function createRunbookHandlers(store, jobStore, inngest2, encryptionKey, schedul
|
|
|
4786
4900
|
createdBy: userId,
|
|
4787
4901
|
startUrl: version.startUrl,
|
|
4788
4902
|
goal: version.goal,
|
|
4789
|
-
context: version.context ?? ""
|
|
4903
|
+
context: version.context ?? "",
|
|
4904
|
+
variables: vStoredVars.length > 0 ? vStoredVars : void 0
|
|
4790
4905
|
});
|
|
4791
4906
|
await store.versions.setVerificationJob(vid, job2.id);
|
|
4792
4907
|
await inngest2.send({
|
|
@@ -4802,6 +4917,7 @@ function createRunbookHandlers(store, jobStore, inngest2, encryptionKey, schedul
|
|
|
4802
4917
|
totalSteps,
|
|
4803
4918
|
purpose: "verification",
|
|
4804
4919
|
createdBy: userId,
|
|
4920
|
+
variables: vStoredVars.length > 0 ? vStoredVars : void 0,
|
|
4805
4921
|
executionConfig: buildExecutionConfig(version.settings)
|
|
4806
4922
|
});
|
|
4807
4923
|
await store.versions.setVerificationJob(vid, job.id);
|
|
@@ -4839,6 +4955,11 @@ function createRunbookHandlers(store, jobStore, inngest2, encryptionKey, schedul
|
|
|
4839
4955
|
if (db) {
|
|
4840
4956
|
await db.delete(runbookSteps).where(eq13(runbookSteps.versionId, vid));
|
|
4841
4957
|
}
|
|
4958
|
+
const vRegenStoredVars = version.variables.filter((v) => v.value != null).map((v) => ({
|
|
4959
|
+
key: v.name,
|
|
4960
|
+
value: v.value,
|
|
4961
|
+
sensitive: v.sensitive
|
|
4962
|
+
}));
|
|
4842
4963
|
const job = await jobStore.insert({
|
|
4843
4964
|
tenantId,
|
|
4844
4965
|
mode: "generate",
|
|
@@ -4848,7 +4969,8 @@ function createRunbookHandlers(store, jobStore, inngest2, encryptionKey, schedul
|
|
|
4848
4969
|
createdBy: userId,
|
|
4849
4970
|
startUrl: version.startUrl,
|
|
4850
4971
|
goal: version.goal,
|
|
4851
|
-
context: version.context ?? void 0
|
|
4972
|
+
context: version.context ?? void 0,
|
|
4973
|
+
variables: vRegenStoredVars.length > 0 ? vRegenStoredVars : void 0
|
|
4852
4974
|
});
|
|
4853
4975
|
await store.versions.setVerificationJob(vid, job.id);
|
|
4854
4976
|
await inngest2.send({
|
|
@@ -4894,7 +5016,7 @@ function createRunbookHandlers(store, jobStore, inngest2, encryptionKey, schedul
|
|
|
4894
5016
|
return c.json({ error: "No pending review found" }, 404);
|
|
4895
5017
|
}
|
|
4896
5018
|
if (body.status === "approved" && userId === pendingReview.requestedBy && db) {
|
|
4897
|
-
const { tenantDevelopmentSettings } = await import("./schema-
|
|
5019
|
+
const { tenantDevelopmentSettings } = await import("./schema-XFSD5EWN.js");
|
|
4898
5020
|
const devSettings = await db.query.tenantDevelopmentSettings.findFirst({
|
|
4899
5021
|
where: eq13(tenantDevelopmentSettings.tenantId, tenantId)
|
|
4900
5022
|
});
|
|
@@ -4915,7 +5037,7 @@ function createRunbookHandlers(store, jobStore, inngest2, encryptionKey, schedul
|
|
|
4915
5037
|
const id = c.req.param("id");
|
|
4916
5038
|
const vid = c.req.param("vid");
|
|
4917
5039
|
if (plan.features.runbookApprovalWorkflow && db) {
|
|
4918
|
-
const { tenantDevelopmentSettings } = await import("./schema-
|
|
5040
|
+
const { tenantDevelopmentSettings } = await import("./schema-XFSD5EWN.js");
|
|
4919
5041
|
const devSettings = await db.query.tenantDevelopmentSettings.findFirst({
|
|
4920
5042
|
where: eq13(tenantDevelopmentSettings.tenantId, tenantId)
|
|
4921
5043
|
});
|
|
@@ -5174,6 +5296,7 @@ var TERMINAL_EVENT_TYPES = /* @__PURE__ */ new Set([
|
|
|
5174
5296
|
var KEEPALIVE_INTERVAL_MS = 3e4;
|
|
5175
5297
|
function handleJobStream(c, jobStore, tenantId, jobId, redisUrl, db) {
|
|
5176
5298
|
const channel = `job:${jobId}:events`;
|
|
5299
|
+
let cleanupRef = null;
|
|
5177
5300
|
const stream = new ReadableStream({
|
|
5178
5301
|
async start(controller) {
|
|
5179
5302
|
const encoder = new TextEncoder();
|
|
@@ -5255,6 +5378,7 @@ function handleJobStream(c, jobStore, tenantId, jobId, redisUrl, db) {
|
|
|
5255
5378
|
} catch {
|
|
5256
5379
|
}
|
|
5257
5380
|
};
|
|
5381
|
+
cleanupRef = cleanup;
|
|
5258
5382
|
try {
|
|
5259
5383
|
subscriber = createRedisSubscriber(redisUrl);
|
|
5260
5384
|
await subscriber.subscribe(channel);
|
|
@@ -5283,6 +5407,9 @@ function handleJobStream(c, jobStore, tenantId, jobId, redisUrl, db) {
|
|
|
5283
5407
|
} catch {
|
|
5284
5408
|
cleanup();
|
|
5285
5409
|
}
|
|
5410
|
+
},
|
|
5411
|
+
cancel() {
|
|
5412
|
+
cleanupRef?.();
|
|
5286
5413
|
}
|
|
5287
5414
|
});
|
|
5288
5415
|
return new Response(stream, {
|
|
@@ -5431,7 +5558,7 @@ function createArtifactHandlers(artifactDb, artifactStore) {
|
|
|
5431
5558
|
}
|
|
5432
5559
|
|
|
5433
5560
|
// src/server/handlers/jobs.ts
|
|
5434
|
-
import { and as and10, eq as eq16, inArray as
|
|
5561
|
+
import { and as and10, eq as eq16, inArray as inArray3 } from "drizzle-orm";
|
|
5435
5562
|
var validStatuses = /* @__PURE__ */ new Set([
|
|
5436
5563
|
"scheduled",
|
|
5437
5564
|
"queued",
|
|
@@ -5493,6 +5620,7 @@ function createJobHandlers(store, runbookStore, inngest2, redisUrl, db, config)
|
|
|
5493
5620
|
const tenantId = c.get("tenantId");
|
|
5494
5621
|
const runbookId = c.req.query("runbookId");
|
|
5495
5622
|
const statusParam = c.req.query("status");
|
|
5623
|
+
const statusGroupParam = c.req.query("statusGroup");
|
|
5496
5624
|
const modeParam = c.req.query("mode");
|
|
5497
5625
|
const limit = Number(c.req.query("limit") ?? "50");
|
|
5498
5626
|
const offset = Number(c.req.query("offset") ?? "0");
|
|
@@ -5503,6 +5631,15 @@ function createJobHandlers(store, runbookStore, inngest2, redisUrl, db, config)
|
|
|
5503
5631
|
}
|
|
5504
5632
|
status = statusParam;
|
|
5505
5633
|
}
|
|
5634
|
+
const statusGroupMap = {
|
|
5635
|
+
running: ["scheduled", "running", "waiting_approval", "exploring", "reviewing", "queued"],
|
|
5636
|
+
completed: ["completed"],
|
|
5637
|
+
failed: ["failed"]
|
|
5638
|
+
};
|
|
5639
|
+
const statuses = statusGroupParam ? statusGroupMap[statusGroupParam] : void 0;
|
|
5640
|
+
if (statusGroupParam && !statuses) {
|
|
5641
|
+
return c.json({ error: `Invalid statusGroup: ${statusGroupParam}` }, 400);
|
|
5642
|
+
}
|
|
5506
5643
|
let mode;
|
|
5507
5644
|
if (modeParam) {
|
|
5508
5645
|
if (!validModes.has(modeParam)) {
|
|
@@ -5513,6 +5650,7 @@ function createJobHandlers(store, runbookStore, inngest2, redisUrl, db, config)
|
|
|
5513
5650
|
const result = await store.list(tenantId, {
|
|
5514
5651
|
runbookId,
|
|
5515
5652
|
status,
|
|
5653
|
+
statuses,
|
|
5516
5654
|
mode,
|
|
5517
5655
|
limit,
|
|
5518
5656
|
offset
|
|
@@ -5520,13 +5658,13 @@ function createJobHandlers(store, runbookStore, inngest2, redisUrl, db, config)
|
|
|
5520
5658
|
const runbookIds = [...new Set(result.items.filter((r) => r.runbookId).map((r) => r.runbookId))];
|
|
5521
5659
|
let titleMap = /* @__PURE__ */ new Map();
|
|
5522
5660
|
if (runbookIds.length > 0 && db) {
|
|
5523
|
-
const rbRows = await db.select({ id: runbooks.id, title: runbooks.title }).from(runbooks).where(
|
|
5661
|
+
const rbRows = await db.select({ id: runbooks.id, title: runbooks.title }).from(runbooks).where(inArray3(runbooks.id, runbookIds));
|
|
5524
5662
|
titleMap = new Map(rbRows.map((r) => [r.id, r.title]));
|
|
5525
5663
|
}
|
|
5526
5664
|
const versionIds = [...new Set(result.items.filter((r) => r.runbookVersionId).map((r) => r.runbookVersionId))];
|
|
5527
5665
|
let versionMap = /* @__PURE__ */ new Map();
|
|
5528
5666
|
if (versionIds.length > 0 && db) {
|
|
5529
|
-
const vRows = await db.select({ id: runbookVersions.id, versionNumber: runbookVersions.versionNumber }).from(runbookVersions).where(
|
|
5667
|
+
const vRows = await db.select({ id: runbookVersions.id, versionNumber: runbookVersions.versionNumber }).from(runbookVersions).where(inArray3(runbookVersions.id, versionIds));
|
|
5530
5668
|
versionMap = new Map(vRows.map((r) => [r.id, r.versionNumber]));
|
|
5531
5669
|
}
|
|
5532
5670
|
const jobIds = result.items.map((r) => r.id);
|
|
@@ -5739,14 +5877,17 @@ function createJobHandlers(store, runbookStore, inngest2, redisUrl, db, config)
|
|
|
5739
5877
|
return c.json({ error: "Job must be completed or failed to apply fix" }, 400);
|
|
5740
5878
|
}
|
|
5741
5879
|
const body = await c.req.json();
|
|
5742
|
-
|
|
5743
|
-
|
|
5880
|
+
const indices = body.suggestionIndices ?? (typeof body.suggestionIndex === "number" ? [body.suggestionIndex] : null);
|
|
5881
|
+
if (!indices || indices.length === 0) {
|
|
5882
|
+
return c.json({ error: "suggestionIndex or suggestionIndices is required" }, 400);
|
|
5744
5883
|
}
|
|
5745
5884
|
const suggestions = row.debugSuggestions ?? [];
|
|
5746
|
-
|
|
5747
|
-
|
|
5885
|
+
for (const idx of indices) {
|
|
5886
|
+
if (idx < 0 || idx >= suggestions.length) {
|
|
5887
|
+
return c.json({ error: `Invalid suggestionIndex: ${idx}` }, 400);
|
|
5888
|
+
}
|
|
5748
5889
|
}
|
|
5749
|
-
const suggestion = suggestions[
|
|
5890
|
+
const suggestion = suggestions[indices[0]];
|
|
5750
5891
|
if (!row.runbookId) {
|
|
5751
5892
|
return c.json({ error: "Job has no associated runbook" }, 400);
|
|
5752
5893
|
}
|
|
@@ -5756,31 +5897,40 @@ function createJobHandlers(store, runbookStore, inngest2, redisUrl, db, config)
|
|
|
5756
5897
|
}
|
|
5757
5898
|
try {
|
|
5758
5899
|
const { stringify: yamlStringify2 } = await import("yaml");
|
|
5759
|
-
const { generatePatchCore } = await import("./yaml-patcher-
|
|
5760
|
-
const { runWithModelContext } = await import("./ai-model-
|
|
5761
|
-
const { resolveTenantAIConfig: resolveTenantAIConfig2, buildModelFactory: buildModelFactory2 } = await import("./tenant-ai-config-
|
|
5900
|
+
const { generatePatchCore } = await import("./yaml-patcher-32QBPXT2.js");
|
|
5901
|
+
const { runWithModelContext } = await import("./ai-model-DP5PKGM6.js");
|
|
5902
|
+
const { resolveTenantAIConfig: resolveTenantAIConfig2, buildModelFactory: buildModelFactory2 } = await import("./tenant-ai-config-4NHKRW7O.js");
|
|
5762
5903
|
const { loadServerConfig: loadServerConfig2 } = await import("./config-ZSUNCFXR.js");
|
|
5763
5904
|
const serverConfig = loadServerConfig2();
|
|
5764
5905
|
const parsedRb = rebuildParsedRunbook(rb);
|
|
5765
5906
|
const originalYaml = yamlStringify2(parsedRb);
|
|
5766
5907
|
const aiConfig = await resolveTenantAIConfig2(db, tenantId, serverConfig.encryptionKey);
|
|
5767
5908
|
const modelFactory = await buildModelFactory2(aiConfig);
|
|
5768
|
-
const
|
|
5909
|
+
const { generatePatchPipeline } = await import("./yaml-patcher-32QBPXT2.js");
|
|
5910
|
+
const extractedSuggestions = indices.map((idx) => {
|
|
5911
|
+
const s = suggestions[idx];
|
|
5912
|
+
return {
|
|
5913
|
+
stepOrdinal: s.stepOrdinal ?? 0,
|
|
5914
|
+
description: s.stepDescription ?? "",
|
|
5915
|
+
error: s.error ?? "",
|
|
5916
|
+
yamlFix: s.runbookFix ?? void 0,
|
|
5917
|
+
category: s.failureCategory ?? void 0
|
|
5918
|
+
};
|
|
5919
|
+
});
|
|
5920
|
+
const patchResult = indices.length === 1 ? await runWithModelContext(
|
|
5769
5921
|
aiConfig,
|
|
5770
5922
|
modelFactory,
|
|
5771
|
-
() => generatePatchCore(originalYaml,
|
|
5772
|
-
|
|
5773
|
-
|
|
5774
|
-
|
|
5775
|
-
|
|
5776
|
-
category: suggestion.failureCategory ?? void 0
|
|
5777
|
-
})
|
|
5923
|
+
() => generatePatchCore(originalYaml, extractedSuggestions[0], void 0, body.additionalPrompt)
|
|
5924
|
+
) : await runWithModelContext(
|
|
5925
|
+
aiConfig,
|
|
5926
|
+
modelFactory,
|
|
5927
|
+
() => generatePatchPipeline(originalYaml, extractedSuggestions, void 0, body.additionalPrompt)
|
|
5778
5928
|
);
|
|
5779
5929
|
if (!patchResult.success) {
|
|
5780
5930
|
return c.json({ error: `Patch generation failed: ${patchResult.error}` }, 500);
|
|
5781
5931
|
}
|
|
5782
5932
|
const { parse: yamlParse } = await import("yaml");
|
|
5783
|
-
const { ParsedRunbookSchema: ParsedRunbookSchema2 } = await import("./runbook-schema-
|
|
5933
|
+
const { ParsedRunbookSchema: ParsedRunbookSchema2 } = await import("./runbook-schema-X7DW725O.js");
|
|
5784
5934
|
const patchedDoc = yamlParse(patchResult.patchedYaml);
|
|
5785
5935
|
const validated = ParsedRunbookSchema2.parse(patchedDoc);
|
|
5786
5936
|
const existingVariables = Object.fromEntries(
|
|
@@ -5792,6 +5942,7 @@ function createJobHandlers(store, runbookStore, inngest2, redisUrl, db, config)
|
|
|
5792
5942
|
value: v.value ?? void 0
|
|
5793
5943
|
}])
|
|
5794
5944
|
);
|
|
5945
|
+
validated.title = rb.title;
|
|
5795
5946
|
await runbookStore.update(tenantId, row.runbookId, {
|
|
5796
5947
|
runbook: validated,
|
|
5797
5948
|
variables: existingVariables
|
|
@@ -5803,6 +5954,451 @@ function createJobHandlers(store, runbookStore, inngest2, redisUrl, db, config)
|
|
|
5803
5954
|
}, 500);
|
|
5804
5955
|
}
|
|
5805
5956
|
});
|
|
5957
|
+
app.get("/:id/review-detail", async (c) => {
|
|
5958
|
+
const tenantId = c.get("tenantId");
|
|
5959
|
+
const id = c.req.param("id");
|
|
5960
|
+
const row = await store.findById(tenantId, id);
|
|
5961
|
+
if (!row) {
|
|
5962
|
+
return c.json({ error: "Not found" }, 404);
|
|
5963
|
+
}
|
|
5964
|
+
if (row.mode !== "generate") {
|
|
5965
|
+
return c.json({ error: "review-detail is only available for generate jobs" }, 400);
|
|
5966
|
+
}
|
|
5967
|
+
const reviewResultRow = row.reviewResult;
|
|
5968
|
+
if (!reviewResultRow) {
|
|
5969
|
+
return c.json({ error: "No review result found" }, 404);
|
|
5970
|
+
}
|
|
5971
|
+
const { jobReviewedSteps: jobReviewedSteps2 } = await import("./schema-XFSD5EWN.js");
|
|
5972
|
+
const { eq: eq25 } = await import("drizzle-orm");
|
|
5973
|
+
const reviewedSteps = db ? await db.select().from(jobReviewedSteps2).where(eq25(jobReviewedSteps2.reviewResultId, reviewResultRow.id)) : [];
|
|
5974
|
+
const recorded = row.recordedSteps ?? [];
|
|
5975
|
+
const recordedMap = new Map(recorded.map((s) => [s.ordinal, s]));
|
|
5976
|
+
const items = reviewedSteps.map((rs) => {
|
|
5977
|
+
const original = recordedMap.get(rs.originalOrdinal);
|
|
5978
|
+
return {
|
|
5979
|
+
originalOrdinal: rs.originalOrdinal,
|
|
5980
|
+
keep: rs.keep,
|
|
5981
|
+
removalReason: rs.removalReason,
|
|
5982
|
+
riskLevel: rs.riskLevel ?? "low",
|
|
5983
|
+
requiresConfirmation: rs.requiresConfirmation ?? false,
|
|
5984
|
+
confirmationReason: rs.confirmationReason,
|
|
5985
|
+
originalStep: {
|
|
5986
|
+
description: original?.description ?? "",
|
|
5987
|
+
actionType: original?.actionType ?? "click",
|
|
5988
|
+
url: original?.url ?? void 0
|
|
5989
|
+
}
|
|
5990
|
+
};
|
|
5991
|
+
});
|
|
5992
|
+
return c.json({ items });
|
|
5993
|
+
});
|
|
5994
|
+
app.post("/:id/apply-review", requireRole("developer"), async (c) => {
|
|
5995
|
+
const tenantId = c.get("tenantId");
|
|
5996
|
+
const id = c.req.param("id");
|
|
5997
|
+
const row = await store.findById(tenantId, id);
|
|
5998
|
+
if (!row) {
|
|
5999
|
+
return c.json({ error: "Not found" }, 404);
|
|
6000
|
+
}
|
|
6001
|
+
if (row.mode !== "generate") {
|
|
6002
|
+
return c.json({ error: "apply-review is only available for generate jobs" }, 400);
|
|
6003
|
+
}
|
|
6004
|
+
if (!row.runbookId) {
|
|
6005
|
+
return c.json({ error: "Job has no associated runbook" }, 400);
|
|
6006
|
+
}
|
|
6007
|
+
const body = await c.req.json();
|
|
6008
|
+
if (!body.decisions || body.decisions.length === 0) {
|
|
6009
|
+
return c.json({ error: "decisions array is required" }, 400);
|
|
6010
|
+
}
|
|
6011
|
+
try {
|
|
6012
|
+
const { jobReviewedSteps: jobReviewedSteps2, jobReviewResults: jobReviewResults2 } = await import("./schema-XFSD5EWN.js");
|
|
6013
|
+
const { eq: eq25 } = await import("drizzle-orm");
|
|
6014
|
+
const reviewResultRow = row.reviewResult;
|
|
6015
|
+
if (!reviewResultRow || !db) {
|
|
6016
|
+
return c.json({ error: "No review result found" }, 404);
|
|
6017
|
+
}
|
|
6018
|
+
await db.delete(jobReviewedSteps2).where(eq25(jobReviewedSteps2.reviewResultId, reviewResultRow.id));
|
|
6019
|
+
await db.insert(jobReviewedSteps2).values(
|
|
6020
|
+
body.decisions.map((d) => ({
|
|
6021
|
+
reviewResultId: reviewResultRow.id,
|
|
6022
|
+
originalOrdinal: d.originalOrdinal,
|
|
6023
|
+
keep: d.keep,
|
|
6024
|
+
riskLevel: d.riskLevel,
|
|
6025
|
+
requiresConfirmation: d.requiresConfirmation
|
|
6026
|
+
}))
|
|
6027
|
+
);
|
|
6028
|
+
const rb = await runbookStore.findById(tenantId, row.runbookId);
|
|
6029
|
+
if (!rb) {
|
|
6030
|
+
return c.json({ error: "Associated runbook not found" }, 404);
|
|
6031
|
+
}
|
|
6032
|
+
const { buildRunbookYaml: buildRunbookYaml2 } = await import("./runbook-builder-2ZLE2AEO.js");
|
|
6033
|
+
const dbRows = row.recordedSteps ?? [];
|
|
6034
|
+
const recorded = dbRows.map((r) => ({
|
|
6035
|
+
ordinal: r.ordinal,
|
|
6036
|
+
action: {
|
|
6037
|
+
action: r.actionType,
|
|
6038
|
+
selector: r.selector ?? void 0,
|
|
6039
|
+
value: r.value ?? void 0,
|
|
6040
|
+
description: r.description ?? "",
|
|
6041
|
+
inputCategory: r.inputCategory,
|
|
6042
|
+
variableName: r.variableName ?? void 0,
|
|
6043
|
+
suggestedCaptures: r.suggestedCaptures,
|
|
6044
|
+
script: r.script ?? void 0,
|
|
6045
|
+
extractPrompt: r.extractPrompt ?? void 0,
|
|
6046
|
+
memoryCollection: r.memoryCollection ?? void 0,
|
|
6047
|
+
aggregation: r.aggregation,
|
|
6048
|
+
downloadPath: r.downloadPath ?? void 0,
|
|
6049
|
+
exportCollection: r.exportCollection ?? void 0,
|
|
6050
|
+
exportFormat: r.exportFormat,
|
|
6051
|
+
exportPath: r.exportPath ?? void 0,
|
|
6052
|
+
keys: r.keys ?? void 0
|
|
6053
|
+
},
|
|
6054
|
+
snapshotBefore: r.snapshotBefore ?? "",
|
|
6055
|
+
url: r.url ?? "",
|
|
6056
|
+
success: r.success ?? false,
|
|
6057
|
+
error: r.error ?? void 0,
|
|
6058
|
+
screenshotPath: r.screenshotPath ?? void 0,
|
|
6059
|
+
failureCategory: r.failureCategory,
|
|
6060
|
+
durationMs: r.durationMs ?? void 0
|
|
6061
|
+
}));
|
|
6062
|
+
const reviewResult = {
|
|
6063
|
+
reviewedSteps: body.decisions.map((d) => ({
|
|
6064
|
+
originalOrdinal: d.originalOrdinal,
|
|
6065
|
+
keep: d.keep,
|
|
6066
|
+
removalReason: void 0,
|
|
6067
|
+
riskLevel: d.riskLevel,
|
|
6068
|
+
requiresConfirmation: d.requiresConfirmation
|
|
6069
|
+
})),
|
|
6070
|
+
summary: reviewResultRow.summary ?? ""
|
|
6071
|
+
};
|
|
6072
|
+
const yamlContent = buildRunbookYaml2({
|
|
6073
|
+
title: rb.title,
|
|
6074
|
+
goal: row.goal ?? rb.goal,
|
|
6075
|
+
startUrl: row.startUrl ?? rb.startUrl ?? "",
|
|
6076
|
+
recordedSteps: recorded,
|
|
6077
|
+
goalAchieved: true,
|
|
6078
|
+
stepDelay: rb.settings?.pauseBetweenSteps ?? 500,
|
|
6079
|
+
reviewResult,
|
|
6080
|
+
contextMarkdown: row.context ?? rb.context ?? ""
|
|
6081
|
+
});
|
|
6082
|
+
const { parse: yamlParse } = await import("yaml");
|
|
6083
|
+
const { ParsedRunbookSchema: ParsedRunbookSchema2 } = await import("./runbook-schema-X7DW725O.js");
|
|
6084
|
+
const patchedDoc = yamlParse(yamlContent);
|
|
6085
|
+
const validated = ParsedRunbookSchema2.parse(patchedDoc);
|
|
6086
|
+
await runbookStore.versions.updateVersion(tenantId, row.runbookId, row.runbookVersionId, {
|
|
6087
|
+
goal: validated.metadata.goal,
|
|
6088
|
+
startUrl: validated.metadata.startUrl,
|
|
6089
|
+
title: validated.title,
|
|
6090
|
+
steps: validated.steps,
|
|
6091
|
+
skipVerificationJobClear: true
|
|
6092
|
+
});
|
|
6093
|
+
return c.json({ success: true });
|
|
6094
|
+
} catch (error) {
|
|
6095
|
+
return c.json({
|
|
6096
|
+
error: `Failed to apply review: ${error instanceof Error ? error.message : String(error)}`
|
|
6097
|
+
}, 500);
|
|
6098
|
+
}
|
|
6099
|
+
});
|
|
6100
|
+
app.post("/:id/regenerate-review", requireRole("developer"), async (c) => {
|
|
6101
|
+
const tenantId = c.get("tenantId");
|
|
6102
|
+
const id = c.req.param("id");
|
|
6103
|
+
const row = await store.findById(tenantId, id);
|
|
6104
|
+
if (!row) {
|
|
6105
|
+
return c.json({ error: "Not found" }, 404);
|
|
6106
|
+
}
|
|
6107
|
+
if (row.mode !== "generate") {
|
|
6108
|
+
return c.json({ error: "regenerate-review is only available for generate jobs" }, 400);
|
|
6109
|
+
}
|
|
6110
|
+
const body = await c.req.json();
|
|
6111
|
+
if (!body.additionalPrompt) {
|
|
6112
|
+
return c.json({ error: "additionalPrompt is required" }, 400);
|
|
6113
|
+
}
|
|
6114
|
+
try {
|
|
6115
|
+
const { createReviewPrompt: createReviewPrompt2, reviewResponseSchema: reviewResponseSchema2 } = await import("./prompts-XMJXIITW.js");
|
|
6116
|
+
const { runWithModelContext, trackedGenerateObject: trackedGenerateObject2, getModel: getModel2 } = await import("./ai-model-DP5PKGM6.js");
|
|
6117
|
+
const { resolveTenantAIConfig: resolveTenantAIConfig2, buildModelFactory: buildModelFactory2 } = await import("./tenant-ai-config-4NHKRW7O.js");
|
|
6118
|
+
const { loadServerConfig: loadServerConfig2 } = await import("./config-ZSUNCFXR.js");
|
|
6119
|
+
const serverConfig = loadServerConfig2();
|
|
6120
|
+
const aiConfig = await resolveTenantAIConfig2(db, tenantId, serverConfig.encryptionKey);
|
|
6121
|
+
const modelFactory = await buildModelFactory2(aiConfig);
|
|
6122
|
+
const dbRows2 = row.recordedSteps ?? [];
|
|
6123
|
+
const recorded = dbRows2.map((r) => ({
|
|
6124
|
+
ordinal: r.ordinal,
|
|
6125
|
+
action: {
|
|
6126
|
+
action: r.actionType,
|
|
6127
|
+
selector: r.selector ?? void 0,
|
|
6128
|
+
value: r.value ?? void 0,
|
|
6129
|
+
description: r.description ?? "",
|
|
6130
|
+
inputCategory: r.inputCategory,
|
|
6131
|
+
variableName: r.variableName ?? void 0,
|
|
6132
|
+
suggestedCaptures: r.suggestedCaptures,
|
|
6133
|
+
script: r.script ?? void 0,
|
|
6134
|
+
extractPrompt: r.extractPrompt ?? void 0,
|
|
6135
|
+
memoryCollection: r.memoryCollection ?? void 0,
|
|
6136
|
+
aggregation: r.aggregation,
|
|
6137
|
+
downloadPath: r.downloadPath ?? void 0,
|
|
6138
|
+
exportCollection: r.exportCollection ?? void 0,
|
|
6139
|
+
exportFormat: r.exportFormat,
|
|
6140
|
+
exportPath: r.exportPath ?? void 0,
|
|
6141
|
+
keys: r.keys ?? void 0
|
|
6142
|
+
},
|
|
6143
|
+
snapshotBefore: r.snapshotBefore ?? "",
|
|
6144
|
+
url: r.url ?? "",
|
|
6145
|
+
success: r.success ?? false,
|
|
6146
|
+
error: r.error ?? void 0,
|
|
6147
|
+
screenshotPath: r.screenshotPath ?? void 0,
|
|
6148
|
+
failureCategory: r.failureCategory,
|
|
6149
|
+
durationMs: r.durationMs ?? void 0
|
|
6150
|
+
}));
|
|
6151
|
+
const { system, userPrompt } = createReviewPrompt2(
|
|
6152
|
+
row.goal ?? "",
|
|
6153
|
+
recorded,
|
|
6154
|
+
true
|
|
6155
|
+
);
|
|
6156
|
+
const enrichedPrompt = `${userPrompt}
|
|
6157
|
+
|
|
6158
|
+
## Additional Instructions from User
|
|
6159
|
+
${body.additionalPrompt}`;
|
|
6160
|
+
const result = await runWithModelContext(aiConfig, modelFactory, async () => {
|
|
6161
|
+
const { object } = await trackedGenerateObject2("review", {
|
|
6162
|
+
model: getModel2("review"),
|
|
6163
|
+
schema: reviewResponseSchema2,
|
|
6164
|
+
messages: [
|
|
6165
|
+
{
|
|
6166
|
+
role: "system",
|
|
6167
|
+
content: system,
|
|
6168
|
+
providerOptions: {
|
|
6169
|
+
anthropic: { cacheControl: { type: "ephemeral" } }
|
|
6170
|
+
}
|
|
6171
|
+
},
|
|
6172
|
+
{ role: "user", content: enrichedPrompt }
|
|
6173
|
+
],
|
|
6174
|
+
temperature: 0
|
|
6175
|
+
});
|
|
6176
|
+
return object;
|
|
6177
|
+
});
|
|
6178
|
+
const parsedResult = reviewResponseSchema2.parse(result);
|
|
6179
|
+
const reviewResultRow = row.reviewResult;
|
|
6180
|
+
if (reviewResultRow && db) {
|
|
6181
|
+
const { jobReviewedSteps: jobReviewedSteps2 } = await import("./schema-XFSD5EWN.js");
|
|
6182
|
+
const { eq: eq25 } = await import("drizzle-orm");
|
|
6183
|
+
await db.delete(jobReviewedSteps2).where(eq25(jobReviewedSteps2.reviewResultId, reviewResultRow.id));
|
|
6184
|
+
await db.insert(jobReviewedSteps2).values(
|
|
6185
|
+
parsedResult.reviewedSteps.map((s) => ({
|
|
6186
|
+
reviewResultId: reviewResultRow.id,
|
|
6187
|
+
originalOrdinal: s.originalOrdinal,
|
|
6188
|
+
keep: s.keep,
|
|
6189
|
+
removalReason: s.removalReason ?? null,
|
|
6190
|
+
riskLevel: s.riskLevel,
|
|
6191
|
+
requiresConfirmation: s.requiresConfirmation,
|
|
6192
|
+
confirmationReason: s.confirmationReason ?? null
|
|
6193
|
+
}))
|
|
6194
|
+
);
|
|
6195
|
+
}
|
|
6196
|
+
const typedRecorded = recorded;
|
|
6197
|
+
const recordedMap = new Map(typedRecorded.map((s) => [s.ordinal, s]));
|
|
6198
|
+
const items = parsedResult.reviewedSteps.map((rs) => {
|
|
6199
|
+
const original = recordedMap.get(rs.originalOrdinal);
|
|
6200
|
+
return {
|
|
6201
|
+
...rs,
|
|
6202
|
+
originalStep: {
|
|
6203
|
+
description: original?.description ?? "",
|
|
6204
|
+
actionType: original?.actionType ?? "click",
|
|
6205
|
+
url: original?.url ?? void 0
|
|
6206
|
+
}
|
|
6207
|
+
};
|
|
6208
|
+
});
|
|
6209
|
+
return c.json({ items });
|
|
6210
|
+
} catch (error) {
|
|
6211
|
+
return c.json({
|
|
6212
|
+
error: `Failed to regenerate review: ${error instanceof Error ? error.message : String(error)}`
|
|
6213
|
+
}, 500);
|
|
6214
|
+
}
|
|
6215
|
+
});
|
|
6216
|
+
app.post("/:id/preview-fix", requireRole("developer"), async (c) => {
|
|
6217
|
+
const tenantId = c.get("tenantId");
|
|
6218
|
+
const id = c.req.param("id");
|
|
6219
|
+
const row = await store.findById(tenantId, id);
|
|
6220
|
+
if (!row) {
|
|
6221
|
+
return c.json({ error: "Not found" }, 404);
|
|
6222
|
+
}
|
|
6223
|
+
if (row.mode !== "self_heal") {
|
|
6224
|
+
return c.json({ error: "preview-fix is only available for self_heal jobs" }, 400);
|
|
6225
|
+
}
|
|
6226
|
+
if (!row.runbookId) {
|
|
6227
|
+
return c.json({ error: "Job has no associated runbook" }, 400);
|
|
6228
|
+
}
|
|
6229
|
+
const body = await c.req.json();
|
|
6230
|
+
const suggestions = row.debugSuggestions ?? [];
|
|
6231
|
+
for (const idx of body.suggestionIndices) {
|
|
6232
|
+
if (idx < 0 || idx >= suggestions.length) {
|
|
6233
|
+
return c.json({ error: `Invalid suggestionIndex: ${idx}` }, 400);
|
|
6234
|
+
}
|
|
6235
|
+
}
|
|
6236
|
+
try {
|
|
6237
|
+
const rb = await runbookStore.findById(tenantId, row.runbookId);
|
|
6238
|
+
if (!rb) {
|
|
6239
|
+
return c.json({ error: "Associated runbook not found" }, 404);
|
|
6240
|
+
}
|
|
6241
|
+
const { stringify: yamlStringify2, parse: yamlParse } = await import("yaml");
|
|
6242
|
+
const { generatePatchCore, generatePatchPipeline } = await import("./yaml-patcher-32QBPXT2.js");
|
|
6243
|
+
const { runWithModelContext } = await import("./ai-model-DP5PKGM6.js");
|
|
6244
|
+
const { resolveTenantAIConfig: resolveTenantAIConfig2, buildModelFactory: buildModelFactory2 } = await import("./tenant-ai-config-4NHKRW7O.js");
|
|
6245
|
+
const { loadServerConfig: loadServerConfig2 } = await import("./config-ZSUNCFXR.js");
|
|
6246
|
+
const serverConfig = loadServerConfig2();
|
|
6247
|
+
const parsedRb = rebuildParsedRunbook(rb);
|
|
6248
|
+
const originalYaml = yamlStringify2(parsedRb);
|
|
6249
|
+
const aiConfig = await resolveTenantAIConfig2(db, tenantId, serverConfig.encryptionKey);
|
|
6250
|
+
const modelFactory = await buildModelFactory2(aiConfig);
|
|
6251
|
+
const extractedSuggestions = body.suggestionIndices.map((idx) => {
|
|
6252
|
+
const s = suggestions[idx];
|
|
6253
|
+
return {
|
|
6254
|
+
stepOrdinal: s.stepOrdinal ?? 0,
|
|
6255
|
+
description: s.stepDescription ?? "",
|
|
6256
|
+
error: s.error ?? "",
|
|
6257
|
+
yamlFix: s.runbookFix ?? void 0,
|
|
6258
|
+
category: s.failureCategory ?? void 0
|
|
6259
|
+
};
|
|
6260
|
+
});
|
|
6261
|
+
const patchResult = extractedSuggestions.length === 1 ? await runWithModelContext(
|
|
6262
|
+
aiConfig,
|
|
6263
|
+
modelFactory,
|
|
6264
|
+
() => generatePatchCore(originalYaml, extractedSuggestions[0], void 0, body.additionalPrompt)
|
|
6265
|
+
) : await runWithModelContext(
|
|
6266
|
+
aiConfig,
|
|
6267
|
+
modelFactory,
|
|
6268
|
+
() => generatePatchPipeline(originalYaml, extractedSuggestions, void 0, body.additionalPrompt)
|
|
6269
|
+
);
|
|
6270
|
+
if (!patchResult.success) {
|
|
6271
|
+
return c.json({ error: `Preview generation failed: ${patchResult.error}` }, 500);
|
|
6272
|
+
}
|
|
6273
|
+
const originalDoc = yamlParse(originalYaml);
|
|
6274
|
+
const patchedDoc = yamlParse(patchResult.patchedYaml);
|
|
6275
|
+
const originalSteps = originalDoc.steps ?? [];
|
|
6276
|
+
const patchedSteps = patchedDoc.steps ?? [];
|
|
6277
|
+
const affectedSteps = [];
|
|
6278
|
+
const patchedMap = new Map(patchedSteps.map((s) => [s.ordinal, s]));
|
|
6279
|
+
const originalMap = new Map(originalSteps.map((s) => [s.ordinal, s]));
|
|
6280
|
+
for (const s of originalSteps) {
|
|
6281
|
+
if (!patchedMap.has(s.ordinal)) {
|
|
6282
|
+
affectedSteps.push({ ordinal: s.ordinal, description: s.description, changeType: "removed" });
|
|
6283
|
+
} else {
|
|
6284
|
+
const patched = patchedMap.get(s.ordinal);
|
|
6285
|
+
if (JSON.stringify(s) !== JSON.stringify(patched)) {
|
|
6286
|
+
affectedSteps.push({ ordinal: s.ordinal, description: patched.description, changeType: "modified" });
|
|
6287
|
+
}
|
|
6288
|
+
}
|
|
6289
|
+
}
|
|
6290
|
+
for (const s of patchedSteps) {
|
|
6291
|
+
if (!originalMap.has(s.ordinal)) {
|
|
6292
|
+
affectedSteps.push({ ordinal: s.ordinal, description: s.description, changeType: "added" });
|
|
6293
|
+
}
|
|
6294
|
+
}
|
|
6295
|
+
return c.json({
|
|
6296
|
+
originalYaml,
|
|
6297
|
+
patchedYaml: patchResult.patchedYaml,
|
|
6298
|
+
affectedSteps
|
|
6299
|
+
});
|
|
6300
|
+
} catch (error) {
|
|
6301
|
+
return c.json({
|
|
6302
|
+
error: `Preview failed: ${error instanceof Error ? error.message : String(error)}`
|
|
6303
|
+
}, 500);
|
|
6304
|
+
}
|
|
6305
|
+
});
|
|
6306
|
+
app.post("/:id/regenerate-suggestions", requireRole("developer"), async (c) => {
|
|
6307
|
+
const tenantId = c.get("tenantId");
|
|
6308
|
+
const id = c.req.param("id");
|
|
6309
|
+
const row = await store.findById(tenantId, id);
|
|
6310
|
+
if (!row) {
|
|
6311
|
+
return c.json({ error: "Not found" }, 404);
|
|
6312
|
+
}
|
|
6313
|
+
if (row.mode !== "self_heal") {
|
|
6314
|
+
return c.json({ error: "regenerate-suggestions is only available for self_heal jobs" }, 400);
|
|
6315
|
+
}
|
|
6316
|
+
const body = await c.req.json();
|
|
6317
|
+
if (!body.additionalPrompt) {
|
|
6318
|
+
return c.json({ error: "additionalPrompt is required" }, 400);
|
|
6319
|
+
}
|
|
6320
|
+
try {
|
|
6321
|
+
if (!row.runbookId) {
|
|
6322
|
+
return c.json({ error: "Job has no associated runbook" }, 400);
|
|
6323
|
+
}
|
|
6324
|
+
const rb = await runbookStore.findById(tenantId, row.runbookId);
|
|
6325
|
+
if (!rb) {
|
|
6326
|
+
return c.json({ error: "Associated runbook not found" }, 404);
|
|
6327
|
+
}
|
|
6328
|
+
const { stringify: yamlStringify2 } = await import("yaml");
|
|
6329
|
+
const { runWithModelContext, trackedGenerateText, getModel: getModel2 } = await import("./ai-model-DP5PKGM6.js");
|
|
6330
|
+
const { resolveTenantAIConfig: resolveTenantAIConfig2, buildModelFactory: buildModelFactory2 } = await import("./tenant-ai-config-4NHKRW7O.js");
|
|
6331
|
+
const { loadServerConfig: loadServerConfig2 } = await import("./config-ZSUNCFXR.js");
|
|
6332
|
+
const serverConfig = loadServerConfig2();
|
|
6333
|
+
const aiConfig = await resolveTenantAIConfig2(db, tenantId, serverConfig.encryptionKey);
|
|
6334
|
+
const modelFactory = await buildModelFactory2(aiConfig);
|
|
6335
|
+
const parsedRb = rebuildParsedRunbook(rb);
|
|
6336
|
+
const yamlContent = yamlStringify2(parsedRb);
|
|
6337
|
+
const results = row.results ?? [];
|
|
6338
|
+
const failedSteps = results.filter(
|
|
6339
|
+
(r) => r.status === "failed" && (!body.stepOrdinals || body.stepOrdinals.includes(r.ordinal))
|
|
6340
|
+
);
|
|
6341
|
+
const stepsInfo = failedSteps.map(
|
|
6342
|
+
(r) => `Step #${r.ordinal}: ${r.description ?? ""} \u2014 Error: ${r.error ?? "unknown"}`
|
|
6343
|
+
).join("\n");
|
|
6344
|
+
const prompt = `You are a browser automation diagnostic AI.
|
|
6345
|
+
Analyze the following failed steps and suggest fixes for the runbook YAML.
|
|
6346
|
+
|
|
6347
|
+
## Runbook YAML
|
|
6348
|
+
\`\`\`yaml
|
|
6349
|
+
${yamlContent}
|
|
6350
|
+
\`\`\`
|
|
6351
|
+
|
|
6352
|
+
## Failed Steps
|
|
6353
|
+
${stepsInfo}
|
|
6354
|
+
|
|
6355
|
+
## Additional Instructions
|
|
6356
|
+
${body.additionalPrompt}
|
|
6357
|
+
|
|
6358
|
+
Respond with a JSON array of suggestions, each with:
|
|
6359
|
+
- stepOrdinal (number)
|
|
6360
|
+
- stepDescription (string)
|
|
6361
|
+
- error (string)
|
|
6362
|
+
- runbookFix (string or null) \u2014 suggested YAML fix description
|
|
6363
|
+
- contextFix (string or null) \u2014 suggested context fix
|
|
6364
|
+
- severity ("error" | "warning")
|
|
6365
|
+
- failureCategory (string or null)`;
|
|
6366
|
+
const result = await runWithModelContext(aiConfig, modelFactory, async () => {
|
|
6367
|
+
const { text } = await trackedGenerateText("review", {
|
|
6368
|
+
model: getModel2("review"),
|
|
6369
|
+
messages: [{ role: "user", content: prompt }],
|
|
6370
|
+
temperature: 0
|
|
6371
|
+
});
|
|
6372
|
+
return text;
|
|
6373
|
+
});
|
|
6374
|
+
const jsonMatch = result.match(/\[[\s\S]*\]/);
|
|
6375
|
+
const newSuggestions = jsonMatch ? JSON.parse(jsonMatch[0]) : [];
|
|
6376
|
+
if (db) {
|
|
6377
|
+
const { jobDebugSuggestions: jobDebugSuggestions2 } = await import("./schema-XFSD5EWN.js");
|
|
6378
|
+
const { eq: eq25 } = await import("drizzle-orm");
|
|
6379
|
+
await db.delete(jobDebugSuggestions2).where(eq25(jobDebugSuggestions2.jobId, id));
|
|
6380
|
+
if (newSuggestions.length > 0) {
|
|
6381
|
+
await db.insert(jobDebugSuggestions2).values(
|
|
6382
|
+
newSuggestions.map((s) => ({
|
|
6383
|
+
jobId: id,
|
|
6384
|
+
stepOrdinal: s.stepOrdinal ?? null,
|
|
6385
|
+
stepDescription: s.stepDescription ?? null,
|
|
6386
|
+
error: s.error ?? null,
|
|
6387
|
+
runbookFix: s.runbookFix ?? null,
|
|
6388
|
+
contextFix: s.contextFix ?? null,
|
|
6389
|
+
severity: s.severity ?? null,
|
|
6390
|
+
failureCategory: s.failureCategory ?? null
|
|
6391
|
+
}))
|
|
6392
|
+
);
|
|
6393
|
+
}
|
|
6394
|
+
}
|
|
6395
|
+
return c.json({ suggestions: newSuggestions });
|
|
6396
|
+
} catch (error) {
|
|
6397
|
+
return c.json({
|
|
6398
|
+
error: `Failed to regenerate suggestions: ${error instanceof Error ? error.message : String(error)}`
|
|
6399
|
+
}, 500);
|
|
6400
|
+
}
|
|
6401
|
+
});
|
|
5806
6402
|
app.post("/:id/adopt", requireRole("developer"), async (c) => {
|
|
5807
6403
|
const tenantId = c.get("tenantId");
|
|
5808
6404
|
const userId = c.get("user")?.id;
|
|
@@ -6043,6 +6639,17 @@ function createScheduleHandlers(store, jobStore, db) {
|
|
|
6043
6639
|
if (!isValidCron(body.cron)) {
|
|
6044
6640
|
return c.json({ error: "Invalid cron expression" }, 400);
|
|
6045
6641
|
}
|
|
6642
|
+
if ((body.purpose ?? "execute") !== "self_heal") {
|
|
6643
|
+
const [rbRow] = await db.select({ activeVersionId: runbooks.activeVersionId }).from(runbooks).where(eq17(runbooks.id, body.runbookId)).limit(1);
|
|
6644
|
+
if (rbRow?.activeVersionId) {
|
|
6645
|
+
const rbVarSources = await db.select({ source: runbookVariables.source }).from(runbookVariables).where(eq17(runbookVariables.versionId, rbRow.activeVersionId));
|
|
6646
|
+
if (rbVarSources.some((v) => v.source === "prompt")) {
|
|
6647
|
+
return c.json({
|
|
6648
|
+
error: tfWithLocale("serverErrors.recurringBlockedByPromptVars", reqLocale(c), {})
|
|
6649
|
+
}, 400);
|
|
6650
|
+
}
|
|
6651
|
+
}
|
|
6652
|
+
}
|
|
6046
6653
|
}
|
|
6047
6654
|
if (type === "once") {
|
|
6048
6655
|
if (!body.scheduledAt) {
|
|
@@ -6085,6 +6692,17 @@ function createScheduleHandlers(store, jobStore, db) {
|
|
|
6085
6692
|
]);
|
|
6086
6693
|
const totalSteps = stepCountResult[0]?.cnt ?? 0;
|
|
6087
6694
|
const storedVars = rbVars.filter((v) => v.value != null).map((v) => ({ key: v.name, value: v.value, sensitive: v.sensitive }));
|
|
6695
|
+
if (body.variables) {
|
|
6696
|
+
for (const [key, value] of Object.entries(body.variables)) {
|
|
6697
|
+
const existing = storedVars.find((v) => v.key === key);
|
|
6698
|
+
const varDef = rbVars.find((v) => v.name === key);
|
|
6699
|
+
if (existing) {
|
|
6700
|
+
existing.value = value;
|
|
6701
|
+
} else {
|
|
6702
|
+
storedVars.push({ key, value, sensitive: varDef?.sensitive ?? false });
|
|
6703
|
+
}
|
|
6704
|
+
}
|
|
6705
|
+
}
|
|
6088
6706
|
const job = await jobStore.insert({
|
|
6089
6707
|
tenantId,
|
|
6090
6708
|
mode,
|
|
@@ -6466,15 +7084,18 @@ function tenantsRoutes(db, config) {
|
|
|
6466
7084
|
}
|
|
6467
7085
|
if (body.slug?.trim()) {
|
|
6468
7086
|
const slug = body.slug.trim();
|
|
6469
|
-
const
|
|
6470
|
-
if (
|
|
6471
|
-
|
|
6472
|
-
|
|
6473
|
-
|
|
6474
|
-
|
|
6475
|
-
|
|
7087
|
+
const [current] = await db.select({ slug: tenants.slug }).from(tenants).where(eq20(tenants.id, tenantId)).limit(1);
|
|
7088
|
+
if (current?.slug !== slug) {
|
|
7089
|
+
const validation = validateSlug(slug, locale);
|
|
7090
|
+
if (!validation.valid) {
|
|
7091
|
+
return c.json({ error: validation.reason }, 400);
|
|
7092
|
+
}
|
|
7093
|
+
const [existing] = await db.select({ id: tenants.id }).from(tenants).where(and13(eq20(tenants.slug, slug), ne(tenants.id, tenantId))).limit(1);
|
|
7094
|
+
if (existing) {
|
|
7095
|
+
return c.json({ error: tWithLocale("tenant.slugTaken", locale) }, 409);
|
|
7096
|
+
}
|
|
7097
|
+
updates.slug = slug;
|
|
6476
7098
|
}
|
|
6477
|
-
updates.slug = slug;
|
|
6478
7099
|
}
|
|
6479
7100
|
if (Object.keys(updates).length === 0) {
|
|
6480
7101
|
return c.json({ error: tWithLocale("serverErrors.noFieldsToUpdate", locale) }, 400);
|
|
@@ -6925,7 +7546,7 @@ function usersRoutes(db) {
|
|
|
6925
7546
|
|
|
6926
7547
|
// src/server/routes/settings.ts
|
|
6927
7548
|
import { Hono as Hono9 } from "hono";
|
|
6928
|
-
import { eq as eq22, and as and14 } from "drizzle-orm";
|
|
7549
|
+
import { eq as eq22, and as and14, desc as desc4, count as count5, inArray as inArray4 } from "drizzle-orm";
|
|
6929
7550
|
|
|
6930
7551
|
// src/server/telemetry/debug-report-generator.ts
|
|
6931
7552
|
function generateDebugTelemetryReport(data) {
|
|
@@ -7320,6 +7941,240 @@ function truncate(s, max) {
|
|
|
7320
7941
|
return s.length > max ? `${s.slice(0, max)}...` : s;
|
|
7321
7942
|
}
|
|
7322
7943
|
|
|
7944
|
+
// src/server/telemetry/report-adapter.ts
|
|
7945
|
+
function convertDiagnostics(diag) {
|
|
7946
|
+
if (!diag) return void 0;
|
|
7947
|
+
return {
|
|
7948
|
+
lastSnapshotPreview: diag.lastSnapshotPreview ?? void 0,
|
|
7949
|
+
failureHistory: Array.isArray(diag.failureHistory) ? diag.failureHistory : void 0,
|
|
7950
|
+
lastAiResponseText: diag.lastAiResponseText ?? void 0,
|
|
7951
|
+
recoveryHint: diag.recoveryHint ?? void 0,
|
|
7952
|
+
deterministicResolveResult: diag.deterministicResolveResult ?? void 0,
|
|
7953
|
+
visionFallbackResult: diag.visionFallbackResult,
|
|
7954
|
+
agentFallbackResult: diag.agentFallbackResult,
|
|
7955
|
+
validationWarnings: Array.isArray(diag.validationWarnings) ? diag.validationWarnings : void 0,
|
|
7956
|
+
validationErrors: Array.isArray(diag.validationErrors) ? diag.validationErrors : void 0,
|
|
7957
|
+
stepUrl: diag.stepUrl ?? void 0
|
|
7958
|
+
};
|
|
7959
|
+
}
|
|
7960
|
+
function convertStepResult(row) {
|
|
7961
|
+
return {
|
|
7962
|
+
ordinal: row.ordinal,
|
|
7963
|
+
description: row.description,
|
|
7964
|
+
status: row.status,
|
|
7965
|
+
durationMs: row.durationMs,
|
|
7966
|
+
error: row.error ?? void 0,
|
|
7967
|
+
actionType: row.actionType ?? void 0,
|
|
7968
|
+
retryCount: row.retryCount ?? void 0,
|
|
7969
|
+
failureCategory: row.failureCategory,
|
|
7970
|
+
capturedValues: row.capturedValues,
|
|
7971
|
+
conditionSkipped: row.conditionSkipped ?? void 0,
|
|
7972
|
+
loopIterations: row.loopIterations ?? void 0,
|
|
7973
|
+
branchMatch: row.branchMatch ?? void 0,
|
|
7974
|
+
forEachItemCount: row.forEachItemCount ?? void 0,
|
|
7975
|
+
extractedData: row.extractedData ?? void 0,
|
|
7976
|
+
downloadedFile: row.downloadedFile ?? void 0,
|
|
7977
|
+
exportedFile: row.exportedFile ?? void 0,
|
|
7978
|
+
diagnostics: convertDiagnostics(row.diagnostics)
|
|
7979
|
+
};
|
|
7980
|
+
}
|
|
7981
|
+
function convertAiMetrics(row) {
|
|
7982
|
+
return {
|
|
7983
|
+
totalCalls: row.totalCalls,
|
|
7984
|
+
totalInputTokens: row.totalInputTokens,
|
|
7985
|
+
totalOutputTokens: row.totalOutputTokens,
|
|
7986
|
+
totalCachedInputTokens: row.totalCachedInputTokens,
|
|
7987
|
+
totalCacheCreationTokens: row.totalCacheCreationTokens,
|
|
7988
|
+
cacheHitRate: row.cacheHitRate,
|
|
7989
|
+
estimatedCostUsd: row.estimatedCostUsd,
|
|
7990
|
+
totalDurationMs: row.totalDurationMs,
|
|
7991
|
+
// DB stores only aggregate totals, not per-purpose/model breakdowns
|
|
7992
|
+
byPurpose: {},
|
|
7993
|
+
byModel: {}
|
|
7994
|
+
};
|
|
7995
|
+
}
|
|
7996
|
+
function buildExecutionReportFromDb(job, details, enrichedData) {
|
|
7997
|
+
const steps = details.stepResults.map(convertStepResult);
|
|
7998
|
+
if (enrichedData?.resolutionOutcomes && enrichedData.resolutionOutcomes.length > 0) {
|
|
7999
|
+
const outcomeMap = /* @__PURE__ */ new Map();
|
|
8000
|
+
for (const o of enrichedData.resolutionOutcomes) {
|
|
8001
|
+
outcomeMap.set(o.stepOrdinal, o);
|
|
8002
|
+
}
|
|
8003
|
+
for (const step of steps) {
|
|
8004
|
+
const outcome = outcomeMap.get(step.ordinal);
|
|
8005
|
+
if (outcome) {
|
|
8006
|
+
step.resolveMethod = outcome.resolveMethod ?? void 0;
|
|
8007
|
+
step.deterministicMatchType = outcome.deterministicMatchType ?? void 0;
|
|
8008
|
+
step.deterministicConfidence = outcome.deterministicConfidence ?? void 0;
|
|
8009
|
+
step.contextChunksUsed = outcome.contextChunksUsed ?? void 0;
|
|
8010
|
+
step.failureHintsProvided = outcome.failureHintsProvided ?? void 0;
|
|
8011
|
+
step.workingMemoryTokens = outcome.workingMemoryTokens ?? void 0;
|
|
8012
|
+
step.snapshotElementCount = outcome.snapshotElementCount ?? void 0;
|
|
8013
|
+
}
|
|
8014
|
+
}
|
|
8015
|
+
}
|
|
8016
|
+
if (enrichedData?.failureEvents && enrichedData.failureEvents.length > 0) {
|
|
8017
|
+
const failureMap = /* @__PURE__ */ new Map();
|
|
8018
|
+
for (const f of enrichedData.failureEvents) {
|
|
8019
|
+
failureMap.set(f.stepOrdinal, f);
|
|
8020
|
+
}
|
|
8021
|
+
for (const step of steps) {
|
|
8022
|
+
const fe = failureMap.get(step.ordinal);
|
|
8023
|
+
if (!fe) continue;
|
|
8024
|
+
if (!step.retryDetails && fe.retryDetails && Array.isArray(fe.retryDetails)) {
|
|
8025
|
+
step.retryDetails = fe.retryDetails;
|
|
8026
|
+
}
|
|
8027
|
+
if (!step.diagnostics) {
|
|
8028
|
+
step.diagnostics = {};
|
|
8029
|
+
}
|
|
8030
|
+
if (!step.diagnostics.stepAction && fe.stepAction && typeof fe.stepAction === "object") {
|
|
8031
|
+
step.diagnostics.stepAction = fe.stepAction;
|
|
8032
|
+
}
|
|
8033
|
+
if (!step.diagnostics.executionStrategy && fe.executionStrategy && typeof fe.executionStrategy === "object") {
|
|
8034
|
+
step.diagnostics.executionStrategy = fe.executionStrategy;
|
|
8035
|
+
}
|
|
8036
|
+
}
|
|
8037
|
+
}
|
|
8038
|
+
const succeeded = steps.filter((s) => s.status === "success").length;
|
|
8039
|
+
const failed = steps.filter((s) => s.status === "failed").length;
|
|
8040
|
+
const skipped = steps.filter((s) => s.status === "skipped").length;
|
|
8041
|
+
const totalDurationMs = steps.reduce((sum2, s) => sum2 + s.durationMs, 0);
|
|
8042
|
+
const report = {
|
|
8043
|
+
runbookTitle: job.title,
|
|
8044
|
+
startUrl: job.startUrl,
|
|
8045
|
+
totalSteps: steps.length,
|
|
8046
|
+
executed: succeeded + failed,
|
|
8047
|
+
succeeded,
|
|
8048
|
+
failed,
|
|
8049
|
+
skipped,
|
|
8050
|
+
aborted: false,
|
|
8051
|
+
steps,
|
|
8052
|
+
totalDurationMs,
|
|
8053
|
+
aiMetrics: details.aiMetrics ? convertAiMetrics(details.aiMetrics) : void 0
|
|
8054
|
+
};
|
|
8055
|
+
const generationContext = job.mode === "generate" ? {
|
|
8056
|
+
goal: job.goal,
|
|
8057
|
+
startUrl: job.startUrl,
|
|
8058
|
+
goalAchieved: failed === 0,
|
|
8059
|
+
generatedAt: (/* @__PURE__ */ new Date()).toISOString(),
|
|
8060
|
+
totalSteps: steps.length
|
|
8061
|
+
} : void 0;
|
|
8062
|
+
let cliOptions;
|
|
8063
|
+
if (enrichedData?.executionConfig) {
|
|
8064
|
+
const ec = enrichedData.executionConfig;
|
|
8065
|
+
cliOptions = [
|
|
8066
|
+
{ name: "--default-timeout", value: String(ec.defaultTimeout) },
|
|
8067
|
+
{ name: "--pause-between-steps", value: String(ec.pauseBetweenSteps) },
|
|
8068
|
+
{ name: "--stop-on-error", value: String(ec.stopOnError) },
|
|
8069
|
+
...ec.stepDelay != null ? [{ name: "--step-delay", value: String(ec.stepDelay) }] : [],
|
|
8070
|
+
{ name: "--enable-selector-cache", value: String(ec.enableSelectorCache) },
|
|
8071
|
+
{ name: "--enable-agent-fallback", value: String(ec.enableAgentFallback) },
|
|
8072
|
+
{ name: "--enable-vision-fallback", value: String(ec.enableVisionFallback) },
|
|
8073
|
+
...ec.modelId ? [{ name: "--model", value: ec.modelId }] : [],
|
|
8074
|
+
...ec.modelProvider ? [{ name: "--model-provider", value: ec.modelProvider }] : []
|
|
8075
|
+
];
|
|
8076
|
+
}
|
|
8077
|
+
let telemetryStats;
|
|
8078
|
+
if (enrichedData?.telemetrySummary) {
|
|
8079
|
+
const ts = enrichedData.telemetrySummary;
|
|
8080
|
+
telemetryStats = {
|
|
8081
|
+
resolveMethodCounts: ts.resolveMethodCounts && typeof ts.resolveMethodCounts === "object" ? ts.resolveMethodCounts : {},
|
|
8082
|
+
visionFallbackAttempts: ts.visionFallbackAttempts ?? 0,
|
|
8083
|
+
visionFallbackSuccesses: ts.visionFallbackSuccesses ?? 0,
|
|
8084
|
+
agentFallbackAttempts: ts.agentFallbackAttempts ?? 0,
|
|
8085
|
+
agentFallbackSuccesses: ts.agentFallbackSuccesses ?? 0,
|
|
8086
|
+
stepsWithRetries: ts.stepsWithRetries ?? 0,
|
|
8087
|
+
totalRetryCount: ts.totalRetryCount ?? 0
|
|
8088
|
+
};
|
|
8089
|
+
}
|
|
8090
|
+
let selfHealSuggestions;
|
|
8091
|
+
if (enrichedData?.debugSuggestions && enrichedData.debugSuggestions.length > 0) {
|
|
8092
|
+
selfHealSuggestions = enrichedData.debugSuggestions.filter((s) => s.stepOrdinal != null).map((s) => ({
|
|
8093
|
+
stepOrdinal: s.stepOrdinal,
|
|
8094
|
+
stepDescription: s.stepDescription ?? "",
|
|
8095
|
+
error: s.error ?? "",
|
|
8096
|
+
runbookFix: s.runbookFix ?? "",
|
|
8097
|
+
contextFix: s.contextFix ?? "",
|
|
8098
|
+
severity: s.severity === "warning" ? "warning" : "error",
|
|
8099
|
+
failureCategory: s.failureCategory,
|
|
8100
|
+
suggestedStrategy: s.suggestedStrategy ?? void 0
|
|
8101
|
+
}));
|
|
8102
|
+
}
|
|
8103
|
+
return {
|
|
8104
|
+
runbookPath: "(from database)",
|
|
8105
|
+
report,
|
|
8106
|
+
errorMessage: job.errorMessage ?? void 0,
|
|
8107
|
+
generationContext,
|
|
8108
|
+
cliOptions,
|
|
8109
|
+
telemetryStats,
|
|
8110
|
+
selfHealSuggestions
|
|
8111
|
+
};
|
|
8112
|
+
}
|
|
8113
|
+
function convertDbRecordedStep(row) {
|
|
8114
|
+
const action = {
|
|
8115
|
+
action: row.actionType,
|
|
8116
|
+
selector: row.selector ?? void 0,
|
|
8117
|
+
value: row.value ?? void 0,
|
|
8118
|
+
description: row.description ?? "",
|
|
8119
|
+
inputCategory: row.inputCategory,
|
|
8120
|
+
variableName: row.variableName ?? void 0,
|
|
8121
|
+
suggestedCaptures: row.suggestedCaptures,
|
|
8122
|
+
script: row.script ?? void 0,
|
|
8123
|
+
extractPrompt: row.extractPrompt ?? void 0,
|
|
8124
|
+
memoryCollection: row.memoryCollection ?? void 0,
|
|
8125
|
+
aggregation: row.aggregation,
|
|
8126
|
+
downloadPath: row.downloadPath ?? void 0,
|
|
8127
|
+
exportCollection: row.exportCollection ?? void 0,
|
|
8128
|
+
exportFormat: row.exportFormat,
|
|
8129
|
+
exportPath: row.exportPath ?? void 0,
|
|
8130
|
+
keys: row.keys ?? void 0
|
|
8131
|
+
};
|
|
8132
|
+
return {
|
|
8133
|
+
ordinal: row.ordinal,
|
|
8134
|
+
action,
|
|
8135
|
+
snapshotBefore: row.snapshotBefore ?? "",
|
|
8136
|
+
url: row.url ?? "",
|
|
8137
|
+
success: row.success ?? true,
|
|
8138
|
+
error: row.error ?? void 0,
|
|
8139
|
+
failureCategory: row.failureCategory,
|
|
8140
|
+
durationMs: row.durationMs ?? void 0
|
|
8141
|
+
};
|
|
8142
|
+
}
|
|
8143
|
+
function convertDbReviewedStep(row) {
|
|
8144
|
+
return {
|
|
8145
|
+
originalOrdinal: row.originalOrdinal,
|
|
8146
|
+
keep: row.keep,
|
|
8147
|
+
removalReason: row.removalReason ?? void 0,
|
|
8148
|
+
riskLevel: row.riskLevel ?? "low",
|
|
8149
|
+
requiresConfirmation: row.requiresConfirmation ?? false,
|
|
8150
|
+
confirmationReason: row.confirmationReason ?? void 0
|
|
8151
|
+
};
|
|
8152
|
+
}
|
|
8153
|
+
function buildGenerationReportInputFromDb(job, data) {
|
|
8154
|
+
return {
|
|
8155
|
+
goal: job.goal,
|
|
8156
|
+
startUrl: job.startUrl,
|
|
8157
|
+
goalAchieved: data.reviewResult?.goalAchieved ?? false,
|
|
8158
|
+
recordedSteps: data.recordedSteps.map(convertDbRecordedStep),
|
|
8159
|
+
reviewResult: data.reviewResult ? {
|
|
8160
|
+
summary: data.reviewResult.summary ?? void 0,
|
|
8161
|
+
yamlContent: data.reviewResult.yamlContent ?? void 0,
|
|
8162
|
+
goalAchieved: data.reviewResult.goalAchieved ?? void 0,
|
|
8163
|
+
reviewedSteps: data.reviewedSteps.map(convertDbReviewedStep)
|
|
8164
|
+
} : void 0,
|
|
8165
|
+
generateConfig: data.generateConfig ? {
|
|
8166
|
+
maxIterations: data.generateConfig.maxIterations,
|
|
8167
|
+
modelId: data.generateConfig.modelId ?? void 0,
|
|
8168
|
+
modelProvider: data.generateConfig.modelProvider ?? void 0,
|
|
8169
|
+
headless: data.generateConfig.headless,
|
|
8170
|
+
stealth: data.generateConfig.stealth,
|
|
8171
|
+
snapshotFilter: data.generateConfig.snapshotFilter
|
|
8172
|
+
} : void 0,
|
|
8173
|
+
aiMetrics: data.aiMetrics ? convertAiMetrics(data.aiMetrics) : void 0,
|
|
8174
|
+
errorMessage: job.errorMessage ?? void 0
|
|
8175
|
+
};
|
|
8176
|
+
}
|
|
8177
|
+
|
|
7323
8178
|
// src/server/routes/settings.ts
|
|
7324
8179
|
import { z as z2 } from "zod";
|
|
7325
8180
|
|
|
@@ -7881,7 +8736,7 @@ function createSettingsRoutes(db, config) {
|
|
|
7881
8736
|
app.get("/development", async (c) => {
|
|
7882
8737
|
const tenantId = c.get("tenantId");
|
|
7883
8738
|
if (!tenantId) return c.json({ error: tWithLocale("serverErrors.tenantRequired", reqLocale(c)) }, 403);
|
|
7884
|
-
const { tenantDevelopmentSettings } = await import("./schema-
|
|
8739
|
+
const { tenantDevelopmentSettings } = await import("./schema-XFSD5EWN.js");
|
|
7885
8740
|
const row = await db.query.tenantDevelopmentSettings.findFirst({
|
|
7886
8741
|
where: eq22(tenantDevelopmentSettings.tenantId, tenantId)
|
|
7887
8742
|
});
|
|
@@ -7903,7 +8758,7 @@ function createSettingsRoutes(db, config) {
|
|
|
7903
8758
|
if (!parsed.success) {
|
|
7904
8759
|
return c.json({ error: parsed.error.flatten() }, 400);
|
|
7905
8760
|
}
|
|
7906
|
-
const { tenantDevelopmentSettings } = await import("./schema-
|
|
8761
|
+
const { tenantDevelopmentSettings } = await import("./schema-XFSD5EWN.js");
|
|
7907
8762
|
await db.insert(tenantDevelopmentSettings).values({
|
|
7908
8763
|
tenantId,
|
|
7909
8764
|
requireApprovalBeforePublish: parsed.data.requireApprovalBeforePublish,
|
|
@@ -8058,12 +8913,136 @@ function createSettingsRoutes(db, config) {
|
|
|
8058
8913
|
const token = generateDebugToken(tenantId, signingKey, getDebugPublicKey());
|
|
8059
8914
|
return c.json({ ok: true, token });
|
|
8060
8915
|
});
|
|
8061
|
-
const telemetryStore = new TelemetryStore(db);
|
|
8062
8916
|
function safeInt2(value) {
|
|
8063
8917
|
if (!value) return void 0;
|
|
8064
8918
|
const n = Number(value);
|
|
8065
8919
|
return Number.isFinite(n) && n > 0 ? Math.floor(n) : void 0;
|
|
8066
8920
|
}
|
|
8921
|
+
app.get("/debug/jobs/recent", requireRole("owner", { rejectApiKey: true }), async (c) => {
|
|
8922
|
+
if (config.nodeEnv !== "development") {
|
|
8923
|
+
return c.json({ error: "Debug endpoints are only available in development" }, 403);
|
|
8924
|
+
}
|
|
8925
|
+
const limit = safeInt2(c.req.query("limit")) ?? 20;
|
|
8926
|
+
const offset = safeInt2(c.req.query("offset")) ?? 0;
|
|
8927
|
+
const runbookIdParam = c.req.query("runbookId");
|
|
8928
|
+
const modeParam = c.req.query("mode");
|
|
8929
|
+
const statusGroupParam = c.req.query("statusGroup");
|
|
8930
|
+
const statusGroupMap = {
|
|
8931
|
+
running: ["scheduled", "running", "waiting_approval", "exploring", "reviewing", "queued"],
|
|
8932
|
+
completed: ["completed"],
|
|
8933
|
+
failed: ["failed", "cancelled"]
|
|
8934
|
+
};
|
|
8935
|
+
if (statusGroupParam && !statusGroupMap[statusGroupParam]) {
|
|
8936
|
+
return c.json({ error: `Invalid statusGroup: ${statusGroupParam}` }, 400);
|
|
8937
|
+
}
|
|
8938
|
+
const conditions = [];
|
|
8939
|
+
if (runbookIdParam) conditions.push(eq22(jobs.runbookId, runbookIdParam));
|
|
8940
|
+
if (modeParam) conditions.push(eq22(jobs.mode, modeParam));
|
|
8941
|
+
if (statusGroupParam) conditions.push(inArray4(jobs.status, statusGroupMap[statusGroupParam]));
|
|
8942
|
+
const where = conditions.length > 0 ? and14(...conditions) : void 0;
|
|
8943
|
+
const [rows, [totalRow]] = await Promise.all([
|
|
8944
|
+
db.select({
|
|
8945
|
+
id: jobs.id,
|
|
8946
|
+
runbookId: jobs.runbookId,
|
|
8947
|
+
runbookTitle: runbooks.title,
|
|
8948
|
+
mode: jobs.mode,
|
|
8949
|
+
status: jobs.status,
|
|
8950
|
+
purpose: jobs.purpose,
|
|
8951
|
+
totalSteps: jobs.totalSteps,
|
|
8952
|
+
startUrl: jobs.startUrl,
|
|
8953
|
+
goal: jobs.goal,
|
|
8954
|
+
completedAt: jobs.completedAt,
|
|
8955
|
+
createdAt: jobs.createdAt
|
|
8956
|
+
}).from(jobs).leftJoin(runbooks, eq22(jobs.runbookId, runbooks.id)).where(where).orderBy(desc4(jobs.createdAt)).limit(limit).offset(offset),
|
|
8957
|
+
db.select({ total: count5() }).from(jobs).where(where)
|
|
8958
|
+
]);
|
|
8959
|
+
return c.json({ items: rows, total: totalRow.total });
|
|
8960
|
+
});
|
|
8961
|
+
app.get("/debug/jobs/:jobId/execution-report", requireRole("owner", { rejectApiKey: true }), async (c) => {
|
|
8962
|
+
if (config.nodeEnv !== "development") {
|
|
8963
|
+
return c.json({ error: "Debug endpoints are only available in development" }, 403);
|
|
8964
|
+
}
|
|
8965
|
+
const jobId = c.req.param("jobId");
|
|
8966
|
+
const job = await db.query.jobs.findFirst({
|
|
8967
|
+
where: eq22(jobs.id, jobId)
|
|
8968
|
+
});
|
|
8969
|
+
if (!job) {
|
|
8970
|
+
return c.json({ error: "Job not found" }, 404);
|
|
8971
|
+
}
|
|
8972
|
+
let title = "Unknown";
|
|
8973
|
+
if (job.runbookId) {
|
|
8974
|
+
const rb = await db.query.runbooks.findFirst({
|
|
8975
|
+
where: eq22(runbooks.id, job.runbookId),
|
|
8976
|
+
columns: { title: true }
|
|
8977
|
+
});
|
|
8978
|
+
if (rb) title = rb.title;
|
|
8979
|
+
}
|
|
8980
|
+
let md;
|
|
8981
|
+
if (job.mode === "generate") {
|
|
8982
|
+
const [recordedSteps, reviewResult, generateConfig, aiMetricsRow] = await Promise.all([
|
|
8983
|
+
db.query.jobRecordedSteps.findMany({
|
|
8984
|
+
where: eq22(jobRecordedSteps.jobId, jobId),
|
|
8985
|
+
orderBy: [jobRecordedSteps.ordinal]
|
|
8986
|
+
}),
|
|
8987
|
+
db.query.jobReviewResults.findFirst({
|
|
8988
|
+
where: eq22(jobReviewResults.jobId, jobId)
|
|
8989
|
+
}),
|
|
8990
|
+
db.query.jobGenerateConfigs.findFirst({
|
|
8991
|
+
where: eq22(jobGenerateConfigs.jobId, jobId)
|
|
8992
|
+
}),
|
|
8993
|
+
db.query.jobAiMetrics.findFirst({
|
|
8994
|
+
where: eq22(jobAiMetrics.jobId, jobId)
|
|
8995
|
+
})
|
|
8996
|
+
]);
|
|
8997
|
+
let reviewedSteps = [];
|
|
8998
|
+
if (reviewResult) {
|
|
8999
|
+
reviewedSteps = await db.query.jobReviewedSteps.findMany({
|
|
9000
|
+
where: eq22(jobReviewedSteps.reviewResultId, reviewResult.id)
|
|
9001
|
+
});
|
|
9002
|
+
}
|
|
9003
|
+
const reportInput = buildGenerationReportInputFromDb(
|
|
9004
|
+
{
|
|
9005
|
+
title,
|
|
9006
|
+
startUrl: job.startUrl ?? "",
|
|
9007
|
+
goal: job.goal ?? "",
|
|
9008
|
+
mode: job.mode,
|
|
9009
|
+
errorMessage: job.errorMessage
|
|
9010
|
+
},
|
|
9011
|
+
{ recordedSteps, reviewResult: reviewResult ?? null, reviewedSteps, generateConfig: generateConfig ?? null, aiMetrics: aiMetricsRow ?? null }
|
|
9012
|
+
);
|
|
9013
|
+
md = await generateGenerationReportFile(reportInput);
|
|
9014
|
+
} else {
|
|
9015
|
+
const telemetry = new TelemetryStore(db);
|
|
9016
|
+
const mode = job.mode === "self_heal" ? "self_heal" : "execute";
|
|
9017
|
+
const reportData = await telemetry.getJobReportData(jobId, mode);
|
|
9018
|
+
const data = reportData;
|
|
9019
|
+
const reportInput = buildExecutionReportFromDb(
|
|
9020
|
+
{
|
|
9021
|
+
title,
|
|
9022
|
+
startUrl: job.startUrl ?? "",
|
|
9023
|
+
goal: job.goal ?? "",
|
|
9024
|
+
mode: job.mode,
|
|
9025
|
+
errorMessage: job.errorMessage
|
|
9026
|
+
},
|
|
9027
|
+
{
|
|
9028
|
+
stepResults: data.stepResults ?? [],
|
|
9029
|
+
aiMetrics: reportData.aiMetrics
|
|
9030
|
+
},
|
|
9031
|
+
{
|
|
9032
|
+
executionConfig: data.executionConfig ?? null,
|
|
9033
|
+
telemetrySummary: data.telemetrySummary ?? null,
|
|
9034
|
+
resolutionOutcomes: data.resolutionOutcomes ?? [],
|
|
9035
|
+
failureEvents: data.failureEvents ?? [],
|
|
9036
|
+
debugSuggestions: data.debugSuggestions
|
|
9037
|
+
}
|
|
9038
|
+
);
|
|
9039
|
+
md = await generateExecutionReportFile(reportInput);
|
|
9040
|
+
}
|
|
9041
|
+
return new Response(md, {
|
|
9042
|
+
headers: { "Content-Type": "text/markdown; charset=utf-8" }
|
|
9043
|
+
});
|
|
9044
|
+
});
|
|
9045
|
+
const telemetryStore = new TelemetryStore(db);
|
|
8067
9046
|
app.get("/debug/telemetry/summaries", requireRole("owner", { rejectApiKey: true }), async (c) => {
|
|
8068
9047
|
if (config.nodeEnv !== "development") {
|
|
8069
9048
|
return c.json({ error: "Debug endpoints are only available in development" }, 403);
|
|
@@ -8349,8 +9328,68 @@ function createTelemetryHandlers(db) {
|
|
|
8349
9328
|
return app;
|
|
8350
9329
|
}
|
|
8351
9330
|
|
|
8352
|
-
// src/server/
|
|
9331
|
+
// src/server/handlers/dashboard.ts
|
|
8353
9332
|
import { Hono as Hono12 } from "hono";
|
|
9333
|
+
import { eq as eq23, and as and15, count as count6 } from "drizzle-orm";
|
|
9334
|
+
function createDashboardHandlers(db) {
|
|
9335
|
+
const app = new Hono12();
|
|
9336
|
+
app.get("/inbox", async (c) => {
|
|
9337
|
+
const tenantId = c.get("tenantId");
|
|
9338
|
+
if (!tenantId) {
|
|
9339
|
+
return c.json({ error: "Tenant required" }, 400);
|
|
9340
|
+
}
|
|
9341
|
+
const rows = await db.select({
|
|
9342
|
+
reviewId: runbookVersionReviews.id,
|
|
9343
|
+
versionId: runbookVersionReviews.versionId,
|
|
9344
|
+
runbookId: runbookVersions.runbookId,
|
|
9345
|
+
runbookTitle: runbooks.title,
|
|
9346
|
+
versionNumber: runbookVersions.versionNumber,
|
|
9347
|
+
goal: runbookVersions.goal,
|
|
9348
|
+
requestedBy: runbookVersionReviews.requestedBy,
|
|
9349
|
+
requestedByName: user.name,
|
|
9350
|
+
requestedAt: runbookVersionReviews.requestedAt
|
|
9351
|
+
}).from(runbookVersionReviews).innerJoin(
|
|
9352
|
+
runbookVersions,
|
|
9353
|
+
eq23(runbookVersionReviews.versionId, runbookVersions.id)
|
|
9354
|
+
).innerJoin(runbooks, eq23(runbookVersions.runbookId, runbooks.id)).innerJoin(user, eq23(runbookVersionReviews.requestedBy, user.id)).where(
|
|
9355
|
+
and15(
|
|
9356
|
+
eq23(runbookVersionReviews.status, "pending"),
|
|
9357
|
+
eq23(runbooks.tenantId, tenantId)
|
|
9358
|
+
)
|
|
9359
|
+
);
|
|
9360
|
+
const versionIds = [...new Set(rows.map((r) => r.versionId))];
|
|
9361
|
+
let stepCounts = {};
|
|
9362
|
+
if (versionIds.length > 0) {
|
|
9363
|
+
const counts = await Promise.all(
|
|
9364
|
+
versionIds.map(async (vid) => {
|
|
9365
|
+
const [row] = await db.select({ count: count6() }).from(runbookSteps).where(eq23(runbookSteps.versionId, vid));
|
|
9366
|
+
return { versionId: vid, count: row?.count ?? 0 };
|
|
9367
|
+
})
|
|
9368
|
+
);
|
|
9369
|
+
stepCounts = Object.fromEntries(
|
|
9370
|
+
counts.map((c2) => [c2.versionId, c2.count])
|
|
9371
|
+
);
|
|
9372
|
+
}
|
|
9373
|
+
return c.json({
|
|
9374
|
+
pendingReviews: rows.map((r) => ({
|
|
9375
|
+
reviewId: r.reviewId,
|
|
9376
|
+
versionId: r.versionId,
|
|
9377
|
+
runbookId: r.runbookId,
|
|
9378
|
+
runbookTitle: r.runbookTitle,
|
|
9379
|
+
versionNumber: r.versionNumber,
|
|
9380
|
+
goal: r.goal,
|
|
9381
|
+
stepsCount: stepCounts[r.versionId] ?? 0,
|
|
9382
|
+
requestedBy: r.requestedBy,
|
|
9383
|
+
requestedByName: r.requestedByName,
|
|
9384
|
+
requestedAt: r.requestedAt.toISOString()
|
|
9385
|
+
}))
|
|
9386
|
+
});
|
|
9387
|
+
});
|
|
9388
|
+
return app;
|
|
9389
|
+
}
|
|
9390
|
+
|
|
9391
|
+
// src/server/webhooks/slack.ts
|
|
9392
|
+
import { Hono as Hono13 } from "hono";
|
|
8354
9393
|
import { createHmac, timingSafeEqual } from "crypto";
|
|
8355
9394
|
|
|
8356
9395
|
// src/server/webhooks/approval-parser.ts
|
|
@@ -8375,15 +9414,15 @@ function parseApprovalButtonId(rawId) {
|
|
|
8375
9414
|
}
|
|
8376
9415
|
|
|
8377
9416
|
// src/server/webhooks/credential-resolver.ts
|
|
8378
|
-
import { eq as
|
|
9417
|
+
import { eq as eq24 } from "drizzle-orm";
|
|
8379
9418
|
async function resolveWebhookCredential(deps, jobId, platform) {
|
|
8380
9419
|
const job = await deps.db.query.jobs.findFirst({
|
|
8381
|
-
where:
|
|
9420
|
+
where: eq24(jobs.id, jobId),
|
|
8382
9421
|
columns: { tenantId: true }
|
|
8383
9422
|
});
|
|
8384
9423
|
if (!job) return null;
|
|
8385
9424
|
const settings = await deps.db.query.tenantExecutionSettings.findFirst({
|
|
8386
|
-
where:
|
|
9425
|
+
where: eq24(tenantExecutionSettings.tenantId, job.tenantId),
|
|
8387
9426
|
columns: { notificationCredentials: true }
|
|
8388
9427
|
});
|
|
8389
9428
|
const allCreds = settings?.notificationCredentials;
|
|
@@ -8422,7 +9461,7 @@ async function resolveAllDiscordPublicKeys(deps) {
|
|
|
8422
9461
|
|
|
8423
9462
|
// src/server/webhooks/slack.ts
|
|
8424
9463
|
function createSlackWebhookRoute(deps) {
|
|
8425
|
-
const app = new
|
|
9464
|
+
const app = new Hono13();
|
|
8426
9465
|
app.post("/", async (c) => {
|
|
8427
9466
|
const timestamp = c.req.header("x-slack-request-timestamp");
|
|
8428
9467
|
const signature = c.req.header("x-slack-signature");
|
|
@@ -8478,7 +9517,7 @@ function createSlackWebhookRoute(deps) {
|
|
|
8478
9517
|
}
|
|
8479
9518
|
|
|
8480
9519
|
// src/server/webhooks/teams.ts
|
|
8481
|
-
import { Hono as
|
|
9520
|
+
import { Hono as Hono14 } from "hono";
|
|
8482
9521
|
import { createRemoteJWKSet, jwtVerify } from "jose";
|
|
8483
9522
|
var BOT_FRAMEWORK_OPENID_METADATA = "https://login.botframework.com/v1/.well-known/openidconfiguration";
|
|
8484
9523
|
var BOT_FRAMEWORK_ISSUERS = [
|
|
@@ -8504,7 +9543,7 @@ async function verifyBotFrameworkToken(token, teamsAppId) {
|
|
|
8504
9543
|
});
|
|
8505
9544
|
}
|
|
8506
9545
|
function createTeamsWebhookRoute(deps) {
|
|
8507
|
-
const app = new
|
|
9546
|
+
const app = new Hono14();
|
|
8508
9547
|
app.post("/", async (c) => {
|
|
8509
9548
|
const authHeader = c.req.header("Authorization");
|
|
8510
9549
|
if (!authHeader?.startsWith("Bearer ")) {
|
|
@@ -8544,7 +9583,7 @@ function createTeamsWebhookRoute(deps) {
|
|
|
8544
9583
|
}
|
|
8545
9584
|
|
|
8546
9585
|
// src/server/webhooks/discord.ts
|
|
8547
|
-
import { Hono as
|
|
9586
|
+
import { Hono as Hono15 } from "hono";
|
|
8548
9587
|
import { verify } from "crypto";
|
|
8549
9588
|
var InteractionType = {
|
|
8550
9589
|
PING: 1,
|
|
@@ -8576,7 +9615,7 @@ function verifyDiscordSignature(publicKeyHex, signature, timestamp, body) {
|
|
|
8576
9615
|
}
|
|
8577
9616
|
}
|
|
8578
9617
|
function createDiscordWebhookRoute(deps) {
|
|
8579
|
-
const app = new
|
|
9618
|
+
const app = new Hono15();
|
|
8580
9619
|
app.post("/", async (c) => {
|
|
8581
9620
|
const signature = c.req.header("X-Signature-Ed25519");
|
|
8582
9621
|
const timestamp = c.req.header("X-Signature-Timestamp");
|
|
@@ -8636,7 +9675,7 @@ function createDiscordWebhookRoute(deps) {
|
|
|
8636
9675
|
// src/server/app.ts
|
|
8637
9676
|
function createApp(config, pool, db) {
|
|
8638
9677
|
const auth = createAuth(config, pool, db);
|
|
8639
|
-
const app = new
|
|
9678
|
+
const app = new Hono16();
|
|
8640
9679
|
if (config.nodeEnv !== "test") {
|
|
8641
9680
|
app.use("*", logger());
|
|
8642
9681
|
}
|
|
@@ -8648,7 +9687,7 @@ function createApp(config, pool, db) {
|
|
|
8648
9687
|
})
|
|
8649
9688
|
);
|
|
8650
9689
|
app.use("*", createAuthMiddleware(auth, db));
|
|
8651
|
-
app.on(["POST", "GET"], "/api/auth
|
|
9690
|
+
app.on(["POST", "GET"], "/api/auth/*", (c) => {
|
|
8652
9691
|
return auth.handler(c.req.raw);
|
|
8653
9692
|
});
|
|
8654
9693
|
app.get(
|
|
@@ -8685,13 +9724,14 @@ function createApp(config, pool, db) {
|
|
|
8685
9724
|
const runbookStore = new RunbookStore(db);
|
|
8686
9725
|
const jobStore = new JobStore(db);
|
|
8687
9726
|
const scheduleStore = new ScheduleStore(db);
|
|
8688
|
-
const v1 = new
|
|
9727
|
+
const v1 = new Hono16();
|
|
8689
9728
|
v1.route("/runbooks", createRunbookHandlers(runbookStore, jobStore, inngest, config.encryptionKey, scheduleStore, db, config));
|
|
8690
9729
|
v1.route("/jobs", createJobHandlers(jobStore, runbookStore, inngest, config.redisUrl, db, config));
|
|
8691
9730
|
v1.route("/schedules", createScheduleHandlers(scheduleStore, jobStore, db));
|
|
8692
9731
|
v1.route("/settings", createSettingsRoutes(db, config));
|
|
8693
9732
|
v1.route("/usage", createUsageHandlers(db));
|
|
8694
9733
|
v1.route("/telemetry", createTelemetryHandlers(db));
|
|
9734
|
+
v1.route("/dashboard", createDashboardHandlers(db));
|
|
8695
9735
|
app.route("/api/v1", v1);
|
|
8696
9736
|
app.route("/api/v1/api-keys", apiKeysRoutes(db));
|
|
8697
9737
|
app.route("/api/v1/tenants", tenantsRoutes(db, config));
|
|
@@ -8714,6 +9754,14 @@ function createApp(config, pool, db) {
|
|
|
8714
9754
|
|
|
8715
9755
|
// src/server/index.ts
|
|
8716
9756
|
async function main() {
|
|
9757
|
+
process.on("uncaughtException", (err) => {
|
|
9758
|
+
if (err.code === "EPIPE" || err.code === "ECONNRESET") {
|
|
9759
|
+
console.warn(`[server] Client disconnected (${err.code})`);
|
|
9760
|
+
return;
|
|
9761
|
+
}
|
|
9762
|
+
console.error("[server] Uncaught exception:", err);
|
|
9763
|
+
process.exit(1);
|
|
9764
|
+
});
|
|
8717
9765
|
const config = loadServerConfig();
|
|
8718
9766
|
let db;
|
|
8719
9767
|
try {
|
|
@@ -8775,4 +9823,4 @@ async function main() {
|
|
|
8775
9823
|
process.on("SIGINT", shutdown);
|
|
8776
9824
|
}
|
|
8777
9825
|
main();
|
|
8778
|
-
//# sourceMappingURL=server-
|
|
9826
|
+
//# sourceMappingURL=server-5XARL5N7.js.map
|