@autonoma-ai/planner 0.1.27 → 0.1.29
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +174 -277
- package/dist/index.js +1496 -880
- package/dist/index.js.map +1 -1
- package/package.json +23 -4
package/dist/index.js
CHANGED
|
@@ -549,7 +549,7 @@ var init_theme = __esm({
|
|
|
549
549
|
});
|
|
550
550
|
|
|
551
551
|
// src/ui/components/render-content.ts
|
|
552
|
-
import matter from "gray-matter";
|
|
552
|
+
import matter from "@11ty/gray-matter";
|
|
553
553
|
import { z as z2 } from "zod";
|
|
554
554
|
function renderContent(text2, kind, name) {
|
|
555
555
|
if (memo != null && memo.text === text2 && memo.kind === kind && memo.name === name) return memo.lines;
|
|
@@ -2101,6 +2101,14 @@ var init_prompts = __esm({
|
|
|
2101
2101
|
}
|
|
2102
2102
|
});
|
|
2103
2103
|
|
|
2104
|
+
// ../../packages/types/src/types/previewkit.ts
|
|
2105
|
+
var init_previewkit = __esm({
|
|
2106
|
+
"../../packages/types/src/types/previewkit.ts"() {
|
|
2107
|
+
"use strict";
|
|
2108
|
+
init_esm_shims();
|
|
2109
|
+
}
|
|
2110
|
+
});
|
|
2111
|
+
|
|
2104
2112
|
// ../../packages/types/src/types/step-overlay-points.ts
|
|
2105
2113
|
import { z as z4 } from "zod";
|
|
2106
2114
|
var overlayPointSchema, pointSchema, outputWithPointsSchema;
|
|
@@ -2288,11 +2296,12 @@ function analysisVerdictPlane(category) {
|
|
|
2288
2296
|
const tier = analysisFindingTier(category);
|
|
2289
2297
|
return tier === "bug" || tier === "passed" ? "app_health" : "coverage";
|
|
2290
2298
|
}
|
|
2291
|
-
var analysisVerdictSchema, ANALYSIS_VERDICT, VERDICT_TIER, coverageVerdicts, analysisTestOriginSchema, coverageCategoryCountSchema, coverageSummarySchema, analysisClassificationReportSchema, analysisClassificationSummarySchema, analysisFindingViewSchema, analysisReportDataSchema, analysisIssueKindSchema, analysisIssueSeveritySchema, analysisIssueStatusSchema, resolvedPrimaryScreenshotSchema, analysisIssueSummarySchema, analysisIssueFindingInstanceSchema, analysisIssueDetailSchema, analysisPrCoveredTestSchema, analysisPrIssueSchema, analysisPrNewerRunSchema, analysisForPrSchema, mainOpenProblemSchema, analysisSnapshotIssueChangesSchema;
|
|
2299
|
+
var analysisVerdictSchema, ANALYSIS_VERDICT, VERDICT_TIER, coverageVerdicts, analysisFlowStatusSchema, analysisFlowOwnerSchema, analysisFlowSchema, analysisTestOriginSchema, coverageCategoryCountSchema, coverageSummarySchema, analysisClassificationReportSchema, analysisClassificationSummarySchema, analysisFindingViewSchema, analysisReportDataSchema, analysisIssueKindSchema, analysisIssueSeveritySchema, analysisIssueStatusSchema, resolvedPrimaryScreenshotSchema, analysisIssueSummarySchema, analysisIssueFindingInstanceSchema, analysisIssueDetailSchema, analysisPrCoveredTestSchema, analysisPrIssueSchema, analysisPrNewerRunSchema, analysisForPrSchema, mainOpenProblemSchema, analysisSnapshotIssueChangesSchema;
|
|
2292
2300
|
var init_analysis = __esm({
|
|
2293
2301
|
"../../packages/types/src/schemas/analysis.ts"() {
|
|
2294
2302
|
"use strict";
|
|
2295
2303
|
init_esm_shims();
|
|
2304
|
+
init_previewkit();
|
|
2296
2305
|
init_step_overlay_points();
|
|
2297
2306
|
init_evidence_tokens();
|
|
2298
2307
|
init_investigation_report();
|
|
@@ -2322,6 +2331,26 @@ var init_analysis = __esm({
|
|
|
2322
2331
|
coverageVerdicts = analysisVerdictSchema.options.filter(
|
|
2323
2332
|
(verdict) => analysisVerdictPlane(verdict) === "coverage"
|
|
2324
2333
|
);
|
|
2334
|
+
analysisFlowStatusSchema = z8.enum(["broken", "verified", "partial", "unverified"]);
|
|
2335
|
+
analysisFlowOwnerSchema = z8.enum(["client", "autonoma", "none"]);
|
|
2336
|
+
analysisFlowSchema = z8.object({
|
|
2337
|
+
/** The reader-facing name, authored by the Reporter: "Guest checkout", not `checkout-guest-express-lane`. */
|
|
2338
|
+
title: z8.string().min(1),
|
|
2339
|
+
/** One sentence, authored by the Reporter: what was confirmed, or why it could not be. */
|
|
2340
|
+
detail: z8.string(),
|
|
2341
|
+
status: analysisFlowStatusSchema,
|
|
2342
|
+
owner: analysisFlowOwnerSchema,
|
|
2343
|
+
/** Cited tests whose last-known verdict confirmed the app. */
|
|
2344
|
+
passedCount: z8.number().int().nonnegative(),
|
|
2345
|
+
/** Cited tests whose last-known verdict is a coverage-plane gap. */
|
|
2346
|
+
gapCount: z8.number().int().nonnegative(),
|
|
2347
|
+
/** Cited tests whose last-known verdict is a client bug. */
|
|
2348
|
+
bugCount: z8.number().int().nonnegative(),
|
|
2349
|
+
/** How many cited verdicts came from the run being reported. Zero means the whole flow is carried from earlier. */
|
|
2350
|
+
checkedThisRunCount: z8.number().int().nonnegative(),
|
|
2351
|
+
/** The tests this flow covers. Every test in the branch's map lands in exactly one flow. */
|
|
2352
|
+
testSlugs: z8.array(z8.string())
|
|
2353
|
+
});
|
|
2325
2354
|
analysisTestOriginSchema = z8.enum(["pre_existing", "proposed"]);
|
|
2326
2355
|
coverageCategoryCountSchema = z8.object({
|
|
2327
2356
|
category: analysisVerdictSchema,
|
|
@@ -2417,17 +2446,24 @@ var init_analysis = __esm({
|
|
|
2417
2446
|
/** The Impact Analysis stage's account of why it selected the tests it did (admin-only on the snapshot page). */
|
|
2418
2447
|
impactReasoning: z8.string().optional(),
|
|
2419
2448
|
/**
|
|
2420
|
-
* The Reporter's holistic PR report prose (Markdown), the hero of the PR page and the snapshot per-job view.
|
|
2421
|
-
*
|
|
2422
|
-
*
|
|
2423
|
-
*
|
|
2449
|
+
* The Reporter's holistic PR report prose (Markdown), the hero of the PR page and the snapshot per-job view. Its
|
|
2450
|
+
* inline `evidence:` image tokens resolve against `reportEvidence`; `issue:`/`finding:` link tokens resolve
|
|
2451
|
+
* against the branch's issues and this report's findings.
|
|
2452
|
+
*
|
|
2453
|
+
* Never empty: a report row exists only once the Reporter has authored one, and `finish` refuses prose that
|
|
2454
|
+
* sanitizing emptied.
|
|
2424
2455
|
*/
|
|
2425
|
-
reportMarkdown: z8.string()
|
|
2456
|
+
reportMarkdown: z8.string(),
|
|
2426
2457
|
/**
|
|
2427
|
-
* The Reporter's
|
|
2428
|
-
*
|
|
2458
|
+
* The Reporter's title for the PR as a whole (about eight words). EMPTY on a report written before the Reporter
|
|
2459
|
+
* authored titles - `analysisPrTitle` is the one place that reads it as unauthored, and it also overrides the
|
|
2460
|
+
* authored value for an open bug and for a run that needed no tests.
|
|
2429
2461
|
*/
|
|
2430
|
-
|
|
2462
|
+
title: z8.string(),
|
|
2463
|
+
/** The Reporter's headline: the CUMULATIVE state of the branch in 1-3 plain sentences. Never empty. */
|
|
2464
|
+
headline: z8.string(),
|
|
2465
|
+
/** The branch's flow itemization: what this PR has established and what it has not. Empty on a pre-flows run. */
|
|
2466
|
+
flows: z8.array(analysisFlowSchema),
|
|
2431
2467
|
/** The signed assets `reportMarkdown` may embed inline by `evidence:<assetId>` token (referenced ones only). */
|
|
2432
2468
|
reportEvidence: z8.array(resolvedEvidenceAssetSchema),
|
|
2433
2469
|
/** The persisted app-health verdict for the run (issue-derived at finalize): `client_bug` or `passed`. */
|
|
@@ -2526,8 +2562,16 @@ var init_analysis = __esm({
|
|
|
2526
2562
|
status: z8.literal("complete"),
|
|
2527
2563
|
/** The app-health verdict: `client_bug` when the branch has an open bug issue, else `passed`. */
|
|
2528
2564
|
verdict: analysisVerdictSchema,
|
|
2529
|
-
/** The Reporter's
|
|
2530
|
-
|
|
2565
|
+
/** The Reporter's title for the PR as a whole. Empty on a report written before the Reporter authored one. */
|
|
2566
|
+
title: z8.string(),
|
|
2567
|
+
/** The Reporter's headline: the cumulative state of the branch in 1-3 plain sentences. */
|
|
2568
|
+
headline: z8.string(),
|
|
2569
|
+
/**
|
|
2570
|
+
* The branch's flow itemization: which parts of the app this PR has established and which it has not. A
|
|
2571
|
+
* consumer reading `verdict` alone learns only whether a bug was found - these say how much was covered, which
|
|
2572
|
+
* is the difference between "nothing broke" and "we checked".
|
|
2573
|
+
*/
|
|
2574
|
+
flows: z8.array(analysisFlowSchema),
|
|
2531
2575
|
/** The Reporter's holistic report prose. Its `evidence:` image tokens resolve against `reportEvidence`, and
|
|
2532
2576
|
* its `issue:` tokens against the `issues` below. */
|
|
2533
2577
|
reportMarkdown: z8.string().optional(),
|
|
@@ -3087,6 +3131,97 @@ var init_api_key = __esm({
|
|
|
3087
3131
|
}
|
|
3088
3132
|
});
|
|
3089
3133
|
|
|
3134
|
+
// ../../packages/types/src/schemas/organization.ts
|
|
3135
|
+
import { z as z14 } from "zod";
|
|
3136
|
+
var InvitationStatusSchema, InviteMemberInputSchema, RevokeInvitationInputSchema, RemoveMemberInputSchema, InvitationIdInputSchema, SetActiveOrganizationInputSchema, RenameOrganizationInputSchema, LeaveOrganizationInputSchema, InvitationOutcomeSchema, InvitationPreviewSchema, MyOrganizationSchema, AppSlugOwnerSchema, AppSlugOwnersInputSchema, OrganizationMemberSchema, PendingInvitationSchema;
|
|
3137
|
+
var init_organization = __esm({
|
|
3138
|
+
"../../packages/types/src/schemas/organization.ts"() {
|
|
3139
|
+
"use strict";
|
|
3140
|
+
init_esm_shims();
|
|
3141
|
+
InvitationStatusSchema = z14.enum(["pending", "accepted", "rejected", "canceled"]);
|
|
3142
|
+
InviteMemberInputSchema = z14.object({
|
|
3143
|
+
email: z14.email("Enter a valid email address").trim().toLowerCase()
|
|
3144
|
+
});
|
|
3145
|
+
RevokeInvitationInputSchema = z14.object({
|
|
3146
|
+
invitationId: z14.string().min(1)
|
|
3147
|
+
});
|
|
3148
|
+
RemoveMemberInputSchema = z14.object({
|
|
3149
|
+
userId: z14.string().min(1)
|
|
3150
|
+
});
|
|
3151
|
+
InvitationIdInputSchema = z14.object({
|
|
3152
|
+
invitationId: z14.string().min(1)
|
|
3153
|
+
});
|
|
3154
|
+
SetActiveOrganizationInputSchema = z14.object({
|
|
3155
|
+
organizationId: z14.string().min(1)
|
|
3156
|
+
});
|
|
3157
|
+
RenameOrganizationInputSchema = z14.object({
|
|
3158
|
+
organizationId: z14.string().min(1),
|
|
3159
|
+
name: z14.string().trim().min(1, "Enter a name").max(100, "Keep it under 100 characters")
|
|
3160
|
+
});
|
|
3161
|
+
LeaveOrganizationInputSchema = z14.object({
|
|
3162
|
+
organizationId: z14.string().min(1)
|
|
3163
|
+
});
|
|
3164
|
+
InvitationOutcomeSchema = z14.enum([
|
|
3165
|
+
"joinable",
|
|
3166
|
+
"wrong-account",
|
|
3167
|
+
"already-member",
|
|
3168
|
+
"expired",
|
|
3169
|
+
"revoked",
|
|
3170
|
+
"accepted",
|
|
3171
|
+
"declined"
|
|
3172
|
+
]);
|
|
3173
|
+
InvitationPreviewSchema = z14.object({
|
|
3174
|
+
invitationId: z14.string(),
|
|
3175
|
+
organizationName: z14.string(),
|
|
3176
|
+
inviterName: z14.string(),
|
|
3177
|
+
invitedEmail: z14.string(),
|
|
3178
|
+
outcome: InvitationOutcomeSchema
|
|
3179
|
+
});
|
|
3180
|
+
MyOrganizationSchema = z14.object({
|
|
3181
|
+
id: z14.string(),
|
|
3182
|
+
name: z14.string(),
|
|
3183
|
+
slug: z14.string(),
|
|
3184
|
+
/** Which one the current session is acting as. Session-scoped: switching does not persist. */
|
|
3185
|
+
isActive: z14.boolean(),
|
|
3186
|
+
memberCount: z14.number().int().positive(),
|
|
3187
|
+
applicationCount: z14.number().int().nonnegative(),
|
|
3188
|
+
joinedAt: z14.date(),
|
|
3189
|
+
/**
|
|
3190
|
+
* Why leaving is refused, if it is. `last-organization` - it is their only one, and an
|
|
3191
|
+
* account with no organization cannot reach anything. `last-member` - nobody would be
|
|
3192
|
+
* left who could reach the organization's applications.
|
|
3193
|
+
*/
|
|
3194
|
+
leaveBlockedReason: z14.enum(["last-organization", "last-member"]).optional()
|
|
3195
|
+
});
|
|
3196
|
+
AppSlugOwnerSchema = z14.object({
|
|
3197
|
+
organizationId: z14.string(),
|
|
3198
|
+
organizationName: z14.string(),
|
|
3199
|
+
organizationSlug: z14.string()
|
|
3200
|
+
});
|
|
3201
|
+
AppSlugOwnersInputSchema = z14.object({
|
|
3202
|
+
appSlug: z14.string().min(1)
|
|
3203
|
+
});
|
|
3204
|
+
OrganizationMemberSchema = z14.object({
|
|
3205
|
+
userId: z14.string(),
|
|
3206
|
+
name: z14.string(),
|
|
3207
|
+
email: z14.string(),
|
|
3208
|
+
image: z14.string().optional(),
|
|
3209
|
+
role: z14.string(),
|
|
3210
|
+
joinedAt: z14.date(),
|
|
3211
|
+
/** True for the member making the request - the row that can't be removed. */
|
|
3212
|
+
isSelf: z14.boolean()
|
|
3213
|
+
});
|
|
3214
|
+
PendingInvitationSchema = z14.object({
|
|
3215
|
+
id: z14.string(),
|
|
3216
|
+
email: z14.string(),
|
|
3217
|
+
inviterName: z14.string(),
|
|
3218
|
+
expiresAt: z14.date(),
|
|
3219
|
+
/** Where to send the invitee if the email never arrives and someone wants to paste the link. */
|
|
3220
|
+
acceptUrl: z14.string()
|
|
3221
|
+
});
|
|
3222
|
+
}
|
|
3223
|
+
});
|
|
3224
|
+
|
|
3090
3225
|
// ../../packages/types/src/schemas/previewkit-builtins.ts
|
|
3091
3226
|
function isReservedPreviewkitEnvKey(key) {
|
|
3092
3227
|
return PREVIEWKIT_BUILTIN_ENV_KEYS.has(key);
|
|
@@ -3138,7 +3273,7 @@ var init_previewkit_builtins = __esm({
|
|
|
3138
3273
|
});
|
|
3139
3274
|
|
|
3140
3275
|
// ../../packages/types/src/schemas/secrets.ts
|
|
3141
|
-
import { z as
|
|
3276
|
+
import { z as z15 } from "zod";
|
|
3142
3277
|
var SECRET_KEY_REGEX, APP_NAME_REGEX, SecretKeySchema, AppNameSchema, SecretItemSchema, ListSecretAppsInputSchema, ListSecretsInputSchema, UpsertSecretsInputSchema, DeleteSecretInputSchema, PreviewkitConfigAppSecretsSchema, PreviewkitConfigSecretsSchema;
|
|
3143
3278
|
var init_secrets = __esm({
|
|
3144
3279
|
"../../packages/types/src/schemas/secrets.ts"() {
|
|
@@ -3147,12 +3282,12 @@ var init_secrets = __esm({
|
|
|
3147
3282
|
init_previewkit_builtins();
|
|
3148
3283
|
SECRET_KEY_REGEX = /^[A-Za-z_][A-Za-z0-9_]*$/;
|
|
3149
3284
|
APP_NAME_REGEX = /^[a-z0-9][a-z0-9-]*[a-z0-9]$/;
|
|
3150
|
-
SecretKeySchema =
|
|
3285
|
+
SecretKeySchema = z15.string().min(1).max(256).regex(
|
|
3151
3286
|
SECRET_KEY_REGEX,
|
|
3152
3287
|
"Keys must start with a letter or underscore and contain only letters, numbers, and underscores"
|
|
3153
3288
|
);
|
|
3154
|
-
AppNameSchema =
|
|
3155
|
-
SecretItemSchema =
|
|
3289
|
+
AppNameSchema = z15.string().min(2).max(63).regex(APP_NAME_REGEX, "App name must be lowercase alphanumeric with hyphens (Kubernetes label-compatible)");
|
|
3290
|
+
SecretItemSchema = z15.object({
|
|
3156
3291
|
key: SecretKeySchema.superRefine((key, ctx) => {
|
|
3157
3292
|
if (isReservedPreviewkitEnvKey(key)) {
|
|
3158
3293
|
ctx.addIssue({
|
|
@@ -3166,31 +3301,50 @@ var init_secrets = __esm({
|
|
|
3166
3301
|
});
|
|
3167
3302
|
}
|
|
3168
3303
|
}),
|
|
3169
|
-
value:
|
|
3304
|
+
value: z15.string().min(1).max(65536)
|
|
3170
3305
|
});
|
|
3171
|
-
ListSecretAppsInputSchema =
|
|
3172
|
-
applicationId:
|
|
3306
|
+
ListSecretAppsInputSchema = z15.object({
|
|
3307
|
+
applicationId: z15.string()
|
|
3173
3308
|
});
|
|
3174
|
-
ListSecretsInputSchema =
|
|
3175
|
-
applicationId:
|
|
3309
|
+
ListSecretsInputSchema = z15.object({
|
|
3310
|
+
applicationId: z15.string(),
|
|
3176
3311
|
appName: AppNameSchema
|
|
3177
3312
|
});
|
|
3178
|
-
UpsertSecretsInputSchema =
|
|
3179
|
-
applicationId:
|
|
3313
|
+
UpsertSecretsInputSchema = z15.object({
|
|
3314
|
+
applicationId: z15.string(),
|
|
3180
3315
|
appName: AppNameSchema,
|
|
3181
|
-
items:
|
|
3316
|
+
items: z15.array(SecretItemSchema).min(1).max(200)
|
|
3182
3317
|
});
|
|
3183
|
-
DeleteSecretInputSchema =
|
|
3184
|
-
applicationId:
|
|
3318
|
+
DeleteSecretInputSchema = z15.object({
|
|
3319
|
+
applicationId: z15.string(),
|
|
3185
3320
|
appName: AppNameSchema,
|
|
3186
3321
|
key: SecretKeySchema
|
|
3187
3322
|
});
|
|
3188
|
-
PreviewkitConfigAppSecretsSchema =
|
|
3323
|
+
PreviewkitConfigAppSecretsSchema = z15.object({
|
|
3189
3324
|
appName: AppNameSchema,
|
|
3190
|
-
upserts:
|
|
3191
|
-
deletes:
|
|
3325
|
+
upserts: z15.array(SecretItemSchema).max(200).default([]),
|
|
3326
|
+
deletes: z15.array(SecretKeySchema).max(200).default([])
|
|
3192
3327
|
});
|
|
3193
|
-
PreviewkitConfigSecretsSchema =
|
|
3328
|
+
PreviewkitConfigSecretsSchema = z15.array(PreviewkitConfigAppSecretsSchema).max(50);
|
|
3329
|
+
}
|
|
3330
|
+
});
|
|
3331
|
+
|
|
3332
|
+
// ../../packages/types/src/parse-url.ts
|
|
3333
|
+
var init_parse_url = __esm({
|
|
3334
|
+
"../../packages/types/src/parse-url.ts"() {
|
|
3335
|
+
"use strict";
|
|
3336
|
+
init_esm_shims();
|
|
3337
|
+
}
|
|
3338
|
+
});
|
|
3339
|
+
|
|
3340
|
+
// ../../packages/types/src/sdk-endpoint.ts
|
|
3341
|
+
var DEFAULT_SDK_PATH;
|
|
3342
|
+
var init_sdk_endpoint = __esm({
|
|
3343
|
+
"../../packages/types/src/sdk-endpoint.ts"() {
|
|
3344
|
+
"use strict";
|
|
3345
|
+
init_esm_shims();
|
|
3346
|
+
init_parse_url();
|
|
3347
|
+
DEFAULT_SDK_PATH = "/api/autonoma";
|
|
3194
3348
|
}
|
|
3195
3349
|
});
|
|
3196
3350
|
|
|
@@ -3619,7 +3773,7 @@ var init_previewkit_presets = __esm({
|
|
|
3619
3773
|
});
|
|
3620
3774
|
|
|
3621
3775
|
// ../../packages/types/src/schemas/previewkit-config.ts
|
|
3622
|
-
import { z as
|
|
3776
|
+
import { z as z16 } from "zod";
|
|
3623
3777
|
function buildResourcesSchema(role, allowCustomResources) {
|
|
3624
3778
|
return resourcesInput.transform((input) => {
|
|
3625
3779
|
if (!allowCustomResources || input == null) {
|
|
@@ -3634,86 +3788,96 @@ function buildResourcesSchema(role, allowCustomResources) {
|
|
|
3634
3788
|
});
|
|
3635
3789
|
}
|
|
3636
3790
|
function singleLineCommand(label) {
|
|
3637
|
-
return
|
|
3791
|
+
return z16.string().min(1).regex(/^[^\r\n]+$/, `${label} must be a single line (no line breaks)`);
|
|
3638
3792
|
}
|
|
3639
3793
|
function heredocSafeScript(label) {
|
|
3640
|
-
return
|
|
3794
|
+
return z16.string().min(1).refine(
|
|
3641
3795
|
(value) => !value.split("\n").includes(PREVIEWKIT_BUILD_SCRIPT_HEREDOC),
|
|
3642
3796
|
`${label} cannot contain a line equal to "${PREVIEWKIT_BUILD_SCRIPT_HEREDOC}" (reserved heredoc delimiter)`
|
|
3643
3797
|
);
|
|
3644
3798
|
}
|
|
3645
3799
|
function nodeFrameworkBuildSchema(framework) {
|
|
3646
|
-
return
|
|
3647
|
-
framework:
|
|
3648
|
-
package_manager:
|
|
3649
|
-
node_version:
|
|
3650
|
-
install_command:
|
|
3651
|
-
build_command:
|
|
3652
|
-
run_command:
|
|
3800
|
+
return z16.object({
|
|
3801
|
+
framework: z16.literal(framework),
|
|
3802
|
+
package_manager: z16.enum(["npm", "pnpm", "yarn"]).default("pnpm"),
|
|
3803
|
+
node_version: z16.string().regex(nodeVersionRegex, "must look like 22, 22.5, or 22.5.0").default("22"),
|
|
3804
|
+
install_command: z16.string().min(1).optional(),
|
|
3805
|
+
build_command: z16.string().min(1).optional(),
|
|
3806
|
+
run_command: z16.string().min(1).optional(),
|
|
3653
3807
|
build_context: buildContextSchema
|
|
3654
3808
|
});
|
|
3655
3809
|
}
|
|
3656
3810
|
function buildPreviewConfigSchema(build, allowCustomResources) {
|
|
3657
|
-
const appSchema =
|
|
3658
|
-
name:
|
|
3659
|
-
repository:
|
|
3811
|
+
const appSchema = z16.object({
|
|
3812
|
+
name: z16.string().regex(k8sNameRegex, "Must be a valid Kubernetes name"),
|
|
3813
|
+
repository: z16.string().regex(repoFullNameRegex, "Must be an owner/repo full name").describe(
|
|
3660
3814
|
"The owner/repo full name of the GitHub repository this app builds from. Mandatory even in single-repo setups. Any value other than the application's own repository makes this app a multirepo dependency: that repo is cloned at the branch the branch_convention (or its repositories[] fallback_branch) resolves to."
|
|
3661
3815
|
),
|
|
3662
|
-
path:
|
|
3663
|
-
build_context:
|
|
3664
|
-
dockerfile:
|
|
3816
|
+
path: z16.string().default("."),
|
|
3817
|
+
build_context: z16.string().optional(),
|
|
3818
|
+
dockerfile: z16.string().optional(),
|
|
3665
3819
|
build: build.optional(),
|
|
3666
3820
|
// The preset-based deploy model, mutually exclusive with `build` (enforced
|
|
3667
3821
|
// by the superRefine below). Lowered to a `runtime` Build by the generator.
|
|
3668
3822
|
blueprint: blueprintSchema.optional(),
|
|
3669
3823
|
// The AWS-secret keys to also inject at build time (Docker build args).
|
|
3670
3824
|
// Runtime secret values live in AWS Secrets Manager, never in this document.
|
|
3671
|
-
build_secrets:
|
|
3672
|
-
port:
|
|
3825
|
+
build_secrets: z16.array(z16.string()).default([]),
|
|
3826
|
+
port: z16.number().int().positive(),
|
|
3673
3827
|
// Non-secret variables wired to the topology, resolved at deploy time.
|
|
3674
3828
|
// All user-typed values are secrets (AWS), so they never appear here.
|
|
3675
|
-
connections:
|
|
3676
|
-
command:
|
|
3677
|
-
health_check:
|
|
3678
|
-
primary:
|
|
3679
|
-
// This app serves the Environment Factory handler
|
|
3680
|
-
//
|
|
3681
|
-
//
|
|
3682
|
-
//
|
|
3683
|
-
sdk_implemented:
|
|
3829
|
+
connections: z16.array(connectionSchema).default([]),
|
|
3830
|
+
command: z16.string().optional(),
|
|
3831
|
+
health_check: z16.string().optional(),
|
|
3832
|
+
primary: z16.boolean().optional(),
|
|
3833
|
+
// This app serves the Environment Factory handler, so scenario up/down
|
|
3834
|
+
// calls go to its preview URL. Independent of `primary`: a full-stack app
|
|
3835
|
+
// (Next.js, Rails) is both the browsed frontend and the SDK host, while a
|
|
3836
|
+
// split topology mounts the handler on its API service.
|
|
3837
|
+
sdk_implemented: z16.boolean().optional(),
|
|
3838
|
+
// Where on that app the handler is mounted. Deliberately NO `.default()`:
|
|
3839
|
+
// absent has to stay distinguishable from an explicit `/api/autonoma`,
|
|
3840
|
+
// because it is what tells a caller to leave an already-stored endpoint
|
|
3841
|
+
// URL alone (see `applySdkPath`). Give it a default and every endpoint a
|
|
3842
|
+
// customer registered by hand at another path gets rewritten to the
|
|
3843
|
+
// convention.
|
|
3844
|
+
sdk_path: z16.string().regex(
|
|
3845
|
+
SDK_PATH_PATTERN,
|
|
3846
|
+
`Must be an absolute path with no query or fragment, like "${DEFAULT_SDK_PATH}"`
|
|
3847
|
+
).optional(),
|
|
3684
3848
|
resources: buildResourcesSchema("app", allowCustomResources),
|
|
3685
|
-
depends_on:
|
|
3849
|
+
depends_on: z16.array(z16.string()).optional()
|
|
3686
3850
|
}).superRefine((app, ctx) => {
|
|
3687
3851
|
if (app.build != null && app.blueprint != null) {
|
|
3688
3852
|
ctx.addIssue({
|
|
3689
|
-
code:
|
|
3853
|
+
code: z16.ZodIssueCode.custom,
|
|
3690
3854
|
message: "an app cannot set both `build` and `blueprint` - `blueprint` is the preset-based deploy model, `build` is the manual one",
|
|
3691
3855
|
path: ["blueprint"]
|
|
3692
3856
|
});
|
|
3693
3857
|
}
|
|
3694
3858
|
});
|
|
3695
|
-
const serviceSchema =
|
|
3696
|
-
name:
|
|
3697
|
-
recipe:
|
|
3698
|
-
version:
|
|
3859
|
+
const serviceSchema = z16.object({
|
|
3860
|
+
name: z16.string().regex(k8sNameRegex, "Must be a valid Kubernetes name"),
|
|
3861
|
+
recipe: z16.string(),
|
|
3862
|
+
version: z16.string().optional(),
|
|
3699
3863
|
// Recipe-functional knobs (e.g. postgres user/database, or a docker-image
|
|
3700
3864
|
// service's image/ports/env) live in `options`, validated per-recipe.
|
|
3701
|
-
options:
|
|
3865
|
+
options: z16.record(z16.string(), z16.unknown()).default({}),
|
|
3702
3866
|
// Guided setup for database-recipe services (schema, seed, migrations),
|
|
3703
3867
|
// run with the repo checked out. Empty for non-database services.
|
|
3704
|
-
setup_tasks:
|
|
3868
|
+
setup_tasks: z16.array(databaseSetupTaskSchema).default([]),
|
|
3705
3869
|
resources: buildResourcesSchema("service", allowCustomResources),
|
|
3706
|
-
s3:
|
|
3707
|
-
sqs:
|
|
3708
|
-
sns:
|
|
3870
|
+
s3: z16.boolean().optional(),
|
|
3871
|
+
sqs: z16.boolean().optional(),
|
|
3872
|
+
sns: z16.boolean().optional()
|
|
3709
3873
|
});
|
|
3710
|
-
return
|
|
3711
|
-
version:
|
|
3712
|
-
domain:
|
|
3713
|
-
registry:
|
|
3874
|
+
return z16.object({
|
|
3875
|
+
version: z16.literal(PREVIEW_CONFIG_VERSION),
|
|
3876
|
+
domain: z16.string().optional(),
|
|
3877
|
+
registry: z16.string().optional(),
|
|
3714
3878
|
// Per-repository overrides + deploy provenance; the repo set itself
|
|
3715
3879
|
// is derived from `apps[].repository`. See repositorySettingsSchema.
|
|
3716
|
-
repositories:
|
|
3880
|
+
repositories: z16.array(repositorySettingsSchema).default([]).describe(
|
|
3717
3881
|
"Optional per-repository settings. The topology's repositories are derived from apps[].repository - an entry here only overrides defaults (fallback_branch: which branch to clone when the PR's branch does not exist in that repo; default main)."
|
|
3718
3882
|
),
|
|
3719
3883
|
// Topology-wide: how a dependency repo's branch is derived from the
|
|
@@ -3721,8 +3885,8 @@ function buildPreviewConfigSchema(build, allowCustomResources) {
|
|
|
3721
3885
|
branch_convention: branchConventionSchema.optional().describe(
|
|
3722
3886
|
"How a dependency repo's branch is derived from the PR branch: same_branch_name (default), regex (pattern + replacement rewrite), or manual (always the fallback_branch)."
|
|
3723
3887
|
),
|
|
3724
|
-
apps:
|
|
3725
|
-
services:
|
|
3888
|
+
apps: z16.array(appSchema).min(1, "At least one app is required"),
|
|
3889
|
+
services: z16.array(serviceSchema).default([]),
|
|
3726
3890
|
hooks: hooksSchema
|
|
3727
3891
|
}).superRefine((cfg, ctx) => {
|
|
3728
3892
|
const seen = /* @__PURE__ */ new Map();
|
|
@@ -3730,7 +3894,7 @@ function buildPreviewConfigSchema(build, allowCustomResources) {
|
|
|
3730
3894
|
const existing = seen.get(name);
|
|
3731
3895
|
if (existing != null) {
|
|
3732
3896
|
ctx.addIssue({
|
|
3733
|
-
code:
|
|
3897
|
+
code: z16.ZodIssueCode.custom,
|
|
3734
3898
|
message: `Name "${name}" is used by both a ${existing} and an ${kind} - names must be unique across apps and services`
|
|
3735
3899
|
});
|
|
3736
3900
|
return;
|
|
@@ -3744,7 +3908,7 @@ function buildPreviewConfigSchema(build, allowCustomResources) {
|
|
|
3744
3908
|
const key = settings.repo.toLowerCase();
|
|
3745
3909
|
if (seenRepos.has(key)) {
|
|
3746
3910
|
ctx.addIssue({
|
|
3747
|
-
code:
|
|
3911
|
+
code: z16.ZodIssueCode.custom,
|
|
3748
3912
|
path: ["repositories", index, "repo"],
|
|
3749
3913
|
message: `Repository "${settings.repo}" has more than one settings entry`
|
|
3750
3914
|
});
|
|
@@ -3761,11 +3925,12 @@ function standardResources(role) {
|
|
|
3761
3925
|
memoryLimit: standard.memoryLimit
|
|
3762
3926
|
};
|
|
3763
3927
|
}
|
|
3764
|
-
var STANDARD_RESOURCES, k8sNameRegex, repoFullNameRegex, resourcesInput, branchConventionSchema, repositorySettingsSchema, hookStepSchema, hooksSchema, databaseSetupLocationSchema, databaseSetupTaskSchema, nodeVersionRegex, buildContextSchema, PREVIEWKIT_BUILD_SCRIPT_HEREDOC, imageTagSchema, DEPRECATED_BUILD_FRAMEWORKS, UNSUPPORTED_BUILD_METHOD_MESSAGE, deprecatedBuildArms, authoredBuildArms, authoredBuildSchema, storedBuildSchema, presetBlueprintSchema, dockerfileBlueprintSchema, blueprintSchema, connectionSchema, previewConfigSchema, trustedPreviewConfigSchema, authoringPreviewConfigSchema;
|
|
3928
|
+
var STANDARD_RESOURCES, PREVIEW_CONFIG_VERSION, k8sNameRegex, SDK_PATH_PATTERN, repoFullNameRegex, DEFAULT_DEPENDENCY_FALLBACK_BRANCH, resourcesInput, branchConventionSchema, repositorySettingsSchema, hookStepSchema, hooksSchema, databaseSetupLocationSchema, databaseSetupTaskSchema, nodeVersionRegex, buildContextSchema, PREVIEWKIT_BUILD_SCRIPT_HEREDOC, imageTagSchema, DEPRECATED_BUILD_FRAMEWORKS, UNSUPPORTED_BUILD_METHOD_MESSAGE, deprecatedBuildArms, authoredBuildArms, authoredBuildSchema, storedBuildSchema, presetBlueprintSchema, dockerfileBlueprintSchema, blueprintSchema, connectionSchema, previewConfigSchema, trustedPreviewConfigSchema, authoringPreviewConfigSchema, appRoleSchema, sdkPathDocumentSchema;
|
|
3765
3929
|
var init_previewkit_config = __esm({
|
|
3766
3930
|
"../../packages/types/src/schemas/previewkit-config.ts"() {
|
|
3767
3931
|
"use strict";
|
|
3768
3932
|
init_esm_shims();
|
|
3933
|
+
init_sdk_endpoint();
|
|
3769
3934
|
init_previewkit_builtins();
|
|
3770
3935
|
init_previewkit_node_pm();
|
|
3771
3936
|
init_previewkit_presets();
|
|
@@ -3775,19 +3940,22 @@ var init_previewkit_config = __esm({
|
|
|
3775
3940
|
app: { cpu: "250m", memoryRequest: "512Mi", memoryLimit: "1Gi" },
|
|
3776
3941
|
service: { cpu: "100m", memoryRequest: "256Mi", memoryLimit: "1Gi" }
|
|
3777
3942
|
};
|
|
3943
|
+
PREVIEW_CONFIG_VERSION = 2;
|
|
3778
3944
|
k8sNameRegex = /^[a-z0-9][a-z0-9-]*[a-z0-9]$/;
|
|
3945
|
+
SDK_PATH_PATTERN = /^\/[^\s?#]*$/;
|
|
3779
3946
|
repoFullNameRegex = /^[^/\s]+\/[^/\s]+$/;
|
|
3780
|
-
|
|
3781
|
-
|
|
3782
|
-
|
|
3783
|
-
|
|
3784
|
-
|
|
3947
|
+
DEFAULT_DEPENDENCY_FALLBACK_BRANCH = "main";
|
|
3948
|
+
resourcesInput = z16.object({
|
|
3949
|
+
cpu: z16.string().optional(),
|
|
3950
|
+
memory: z16.string().optional(),
|
|
3951
|
+
memoryRequest: z16.string().optional(),
|
|
3952
|
+
memoryLimit: z16.string().optional()
|
|
3785
3953
|
}).optional();
|
|
3786
|
-
branchConventionSchema =
|
|
3787
|
-
|
|
3788
|
-
|
|
3789
|
-
type:
|
|
3790
|
-
pattern:
|
|
3954
|
+
branchConventionSchema = z16.discriminatedUnion("type", [
|
|
3955
|
+
z16.object({ type: z16.literal("same_branch_name") }),
|
|
3956
|
+
z16.object({
|
|
3957
|
+
type: z16.literal("regex"),
|
|
3958
|
+
pattern: z16.string().refine((pattern) => {
|
|
3791
3959
|
try {
|
|
3792
3960
|
new RegExp(pattern);
|
|
3793
3961
|
return true;
|
|
@@ -3795,13 +3963,13 @@ var init_previewkit_config = __esm({
|
|
|
3795
3963
|
return false;
|
|
3796
3964
|
}
|
|
3797
3965
|
}, "Invalid regular expression pattern"),
|
|
3798
|
-
replacement:
|
|
3966
|
+
replacement: z16.string()
|
|
3799
3967
|
}),
|
|
3800
|
-
|
|
3968
|
+
z16.object({ type: z16.literal("manual") })
|
|
3801
3969
|
]);
|
|
3802
|
-
repositorySettingsSchema =
|
|
3803
|
-
repo:
|
|
3804
|
-
fallback_branch:
|
|
3970
|
+
repositorySettingsSchema = z16.object({
|
|
3971
|
+
repo: z16.string().regex(repoFullNameRegex, "Must be an owner/repo full name"),
|
|
3972
|
+
fallback_branch: z16.string().default(DEFAULT_DEPENDENCY_FALLBACK_BRANCH),
|
|
3805
3973
|
/**
|
|
3806
3974
|
* The concrete commit SHA the repository was deployed at. Absent in
|
|
3807
3975
|
* user-authored config: previewkit resolves each dependency repo's branch to
|
|
@@ -3809,60 +3977,60 @@ var init_previewkit_config = __esm({
|
|
|
3809
3977
|
* `resolvedConfig` (deploy provenance, not authored input). Multi-repo
|
|
3810
3978
|
* grounding reads this back to inspect the exact code that was live.
|
|
3811
3979
|
*/
|
|
3812
|
-
sha:
|
|
3980
|
+
sha: z16.string().optional()
|
|
3813
3981
|
});
|
|
3814
|
-
hookStepSchema =
|
|
3815
|
-
app:
|
|
3816
|
-
command:
|
|
3982
|
+
hookStepSchema = z16.object({
|
|
3983
|
+
app: z16.string(),
|
|
3984
|
+
command: z16.string()
|
|
3817
3985
|
});
|
|
3818
|
-
hooksSchema =
|
|
3819
|
-
pre_deploy:
|
|
3820
|
-
post_deploy:
|
|
3986
|
+
hooksSchema = z16.object({
|
|
3987
|
+
pre_deploy: z16.array(hookStepSchema).default([]),
|
|
3988
|
+
post_deploy: z16.array(hookStepSchema).default([])
|
|
3821
3989
|
}).default({ pre_deploy: [], post_deploy: [] });
|
|
3822
|
-
databaseSetupLocationSchema =
|
|
3823
|
-
|
|
3824
|
-
type:
|
|
3825
|
-
app:
|
|
3826
|
-
position:
|
|
3990
|
+
databaseSetupLocationSchema = z16.discriminatedUnion("type", [
|
|
3991
|
+
z16.object({
|
|
3992
|
+
type: z16.literal("in_build"),
|
|
3993
|
+
app: z16.string().min(1, "an app is required for an in-build task"),
|
|
3994
|
+
position: z16.enum(["before", "after"])
|
|
3827
3995
|
}),
|
|
3828
|
-
|
|
3829
|
-
type:
|
|
3830
|
-
repo:
|
|
3996
|
+
z16.object({
|
|
3997
|
+
type: z16.literal("separate_job"),
|
|
3998
|
+
repo: z16.string().optional()
|
|
3831
3999
|
})
|
|
3832
4000
|
]);
|
|
3833
|
-
databaseSetupTaskSchema =
|
|
3834
|
-
command:
|
|
3835
|
-
frequency:
|
|
4001
|
+
databaseSetupTaskSchema = z16.object({
|
|
4002
|
+
command: z16.string(),
|
|
4003
|
+
frequency: z16.enum(["on_create", "every_commit"]),
|
|
3836
4004
|
location: databaseSetupLocationSchema
|
|
3837
4005
|
});
|
|
3838
4006
|
nodeVersionRegex = /^\d+(\.\d+)?(\.\d+)?$/;
|
|
3839
|
-
buildContextSchema =
|
|
4007
|
+
buildContextSchema = z16.enum(["app", "root"]).default("app");
|
|
3840
4008
|
PREVIEWKIT_BUILD_SCRIPT_HEREDOC = "AUTONOMA_BUILD_EOF";
|
|
3841
|
-
imageTagSchema =
|
|
4009
|
+
imageTagSchema = z16.string().regex(/^[A-Za-z0-9][A-Za-z0-9._-]*$/, "must be a valid image tag");
|
|
3842
4010
|
DEPRECATED_BUILD_FRAMEWORKS = ["node", "next", "vite", "bun"];
|
|
3843
4011
|
UNSUPPORTED_BUILD_METHOD_MESSAGE = `Unsupported build method. Use "runtime" (pick a runtime, then write a bash build_script and a single-line entrypoint) or "dockerfile" (build a Dockerfile committed in the repo). The ${DEPRECATED_BUILD_FRAMEWORKS.join(" / ")} framework presets are retired and can no longer be saved - express them as "runtime" with the install and build commands in build_script and the start command as entrypoint.`;
|
|
3844
4012
|
deprecatedBuildArms = [
|
|
3845
4013
|
nodeFrameworkBuildSchema("node"),
|
|
3846
4014
|
nodeFrameworkBuildSchema("next"),
|
|
3847
4015
|
nodeFrameworkBuildSchema("vite"),
|
|
3848
|
-
|
|
3849
|
-
framework:
|
|
3850
|
-
install_command:
|
|
3851
|
-
build_command:
|
|
3852
|
-
run_command:
|
|
4016
|
+
z16.object({
|
|
4017
|
+
framework: z16.literal("bun"),
|
|
4018
|
+
install_command: z16.string().min(1).optional(),
|
|
4019
|
+
build_command: z16.string().min(1).optional(),
|
|
4020
|
+
run_command: z16.string().min(1).optional(),
|
|
3853
4021
|
build_context: buildContextSchema
|
|
3854
4022
|
})
|
|
3855
4023
|
];
|
|
3856
4024
|
authoredBuildArms = [
|
|
3857
|
-
|
|
3858
|
-
framework:
|
|
3859
|
-
dockerfile:
|
|
3860
|
-
target:
|
|
4025
|
+
z16.object({
|
|
4026
|
+
framework: z16.literal("dockerfile"),
|
|
4027
|
+
dockerfile: z16.string().min(1, "dockerfile path is required"),
|
|
4028
|
+
target: z16.string().min(1).optional(),
|
|
3861
4029
|
build_context: buildContextSchema
|
|
3862
4030
|
}),
|
|
3863
|
-
|
|
3864
|
-
framework:
|
|
3865
|
-
runtime:
|
|
4031
|
+
z16.object({
|
|
4032
|
+
framework: z16.literal("runtime"),
|
|
4033
|
+
runtime: z16.enum(PREVIEWKIT_RUNTIME_IDS),
|
|
3866
4034
|
// Image tag version, e.g. "22" for node. Optional - defaults to the
|
|
3867
4035
|
// catalog's default per runtime. The user picks it so a repo pinned to an
|
|
3868
4036
|
// older toolchain is not forced onto our default (which would defeat the
|
|
@@ -3879,12 +4047,12 @@ var init_previewkit_config = __esm({
|
|
|
3879
4047
|
build_context: buildContextSchema
|
|
3880
4048
|
})
|
|
3881
4049
|
];
|
|
3882
|
-
authoredBuildSchema =
|
|
4050
|
+
authoredBuildSchema = z16.discriminatedUnion("framework", authoredBuildArms, {
|
|
3883
4051
|
error: () => UNSUPPORTED_BUILD_METHOD_MESSAGE
|
|
3884
4052
|
});
|
|
3885
|
-
storedBuildSchema =
|
|
3886
|
-
presetBlueprintSchema =
|
|
3887
|
-
preset:
|
|
4053
|
+
storedBuildSchema = z16.discriminatedUnion("framework", [...authoredBuildArms, ...deprecatedBuildArms]);
|
|
4054
|
+
presetBlueprintSchema = z16.object({
|
|
4055
|
+
preset: z16.enum(PREVIEWKIT_PRESET_IDS),
|
|
3888
4056
|
// Overridable per app; each defaults from the preset (version from the runtime catalog).
|
|
3889
4057
|
version: imageTagSchema.optional(),
|
|
3890
4058
|
// These overrides flow into the generated Dockerfile via blueprintToBuild, so they
|
|
@@ -3904,82 +4072,98 @@ var init_previewkit_config = __esm({
|
|
|
3904
4072
|
const toolchain = previewkitPresetSpec(blueprint.preset).toolchain;
|
|
3905
4073
|
if (toolchain === "node" && blueprint.version != null && !nodeVersionRegex.test(blueprint.version)) {
|
|
3906
4074
|
ctx.addIssue({
|
|
3907
|
-
code:
|
|
4075
|
+
code: z16.ZodIssueCode.custom,
|
|
3908
4076
|
message: "version must look like 22, 22.5, or 22.5.0 for a node preset",
|
|
3909
4077
|
path: ["version"]
|
|
3910
4078
|
});
|
|
3911
4079
|
}
|
|
3912
4080
|
});
|
|
3913
|
-
dockerfileBlueprintSchema =
|
|
4081
|
+
dockerfileBlueprintSchema = z16.object({
|
|
3914
4082
|
// Path to a committed Dockerfile, built as-is - ALWAYS relative to the app dir,
|
|
3915
4083
|
// whatever the build context (the pipeline resolves it against the context).
|
|
3916
4084
|
// `target` selects a stage in a multi-stage build.
|
|
3917
|
-
dockerfile:
|
|
3918
|
-
target:
|
|
4085
|
+
dockerfile: z16.string().min(1, "dockerfile path is required"),
|
|
4086
|
+
target: z16.string().min(1).optional(),
|
|
3919
4087
|
// Monorepo: `root` builds from the repo root (so a workspace-aware Dockerfile can reach
|
|
3920
4088
|
// sibling packages); `app` (absent) builds from the app dir.
|
|
3921
4089
|
build_context: buildContextSchema.removeDefault().optional()
|
|
3922
4090
|
}).strict();
|
|
3923
|
-
blueprintSchema =
|
|
3924
|
-
connectionSchema =
|
|
4091
|
+
blueprintSchema = z16.union([presetBlueprintSchema, dockerfileBlueprintSchema]);
|
|
4092
|
+
connectionSchema = z16.object({
|
|
3925
4093
|
key: SecretKeySchema.superRefine((key, ctx) => {
|
|
3926
4094
|
if (isReservedPreviewkitEnvKey(key)) {
|
|
3927
4095
|
ctx.addIssue({
|
|
3928
|
-
code:
|
|
4096
|
+
code: z16.ZodIssueCode.custom,
|
|
3929
4097
|
message: `${key} is a reserved built-in variable and cannot be set.`
|
|
3930
4098
|
});
|
|
3931
4099
|
}
|
|
3932
4100
|
}).describe("The env var name to inject into the app at runtime (e.g. DATABASE_URL)."),
|
|
3933
|
-
value:
|
|
4101
|
+
value: z16.string().min(1, "value is required").describe(
|
|
3934
4102
|
"Template resolved at deploy time. {{name.property}} tokens reference apps/services in this config by name. For a service: {{db.url}} = the full canonical connection string (postgres -> postgresql://preview:preview@<host>:<port>/preview; redis -> redis://...; mongodb -> mongodb://...), {{db.host}} = in-cluster DNS name, {{db.port}}. For an app: {{api.url}} = its public HTTPS URL, {{api.hostname}}. Also available: {{pr}}, {{namespace}}, {{owner}}. Tokens can mix with literal text ('postgresql://user:pass@{{db.host}}:5432/mydb'), or be a plain literal ('production'). Prefer {{db.url}} to wire a database."
|
|
3935
4103
|
),
|
|
3936
|
-
build_time:
|
|
4104
|
+
build_time: z16.boolean().default(false).describe("Also pass the resolved value as a Docker build arg.")
|
|
3937
4105
|
});
|
|
3938
4106
|
previewConfigSchema = buildPreviewConfigSchema(storedBuildSchema, false);
|
|
3939
4107
|
trustedPreviewConfigSchema = buildPreviewConfigSchema(storedBuildSchema, true);
|
|
3940
4108
|
authoringPreviewConfigSchema = buildPreviewConfigSchema(authoredBuildSchema, false);
|
|
4109
|
+
appRoleSchema = z16.object({
|
|
4110
|
+
name: z16.string(),
|
|
4111
|
+
primary: z16.boolean().nullish(),
|
|
4112
|
+
sdk_implemented: z16.boolean().nullish(),
|
|
4113
|
+
sdk_path: z16.string().nullish()
|
|
4114
|
+
});
|
|
4115
|
+
sdkPathDocumentSchema = z16.object({ apps: z16.array(appRoleSchema) });
|
|
4116
|
+
}
|
|
4117
|
+
});
|
|
4118
|
+
|
|
4119
|
+
// ../../packages/types/src/schemas/previewkit-config-rows.ts
|
|
4120
|
+
var init_previewkit_config_rows = __esm({
|
|
4121
|
+
"../../packages/types/src/schemas/previewkit-config-rows.ts"() {
|
|
4122
|
+
"use strict";
|
|
4123
|
+
init_esm_shims();
|
|
4124
|
+
init_previewkit_config();
|
|
3941
4125
|
}
|
|
3942
4126
|
});
|
|
3943
4127
|
|
|
3944
4128
|
// ../../packages/types/src/schemas/previewkit-job-spec.ts
|
|
3945
|
-
import { z as
|
|
4129
|
+
import { z as z17 } from "zod";
|
|
3946
4130
|
var deployTargetSchema, redeployTargetSchema, teardownTargetSchema, previewJobSpecSchema;
|
|
3947
4131
|
var init_previewkit_job_spec = __esm({
|
|
3948
4132
|
"../../packages/types/src/schemas/previewkit-job-spec.ts"() {
|
|
3949
4133
|
"use strict";
|
|
3950
4134
|
init_esm_shims();
|
|
3951
|
-
deployTargetSchema =
|
|
3952
|
-
repoFullName:
|
|
3953
|
-
prNumber:
|
|
3954
|
-
organizationId:
|
|
3955
|
-
githubRepositoryId:
|
|
3956
|
-
headSha:
|
|
3957
|
-
headRef:
|
|
3958
|
-
branchId:
|
|
4135
|
+
deployTargetSchema = z17.object({
|
|
4136
|
+
repoFullName: z17.string().min(1),
|
|
4137
|
+
prNumber: z17.number().int().nonnegative(),
|
|
4138
|
+
organizationId: z17.string().min(1),
|
|
4139
|
+
githubRepositoryId: z17.number().int(),
|
|
4140
|
+
headSha: z17.string().min(1),
|
|
4141
|
+
headRef: z17.string().min(1),
|
|
4142
|
+
branchId: z17.string().optional()
|
|
3959
4143
|
});
|
|
3960
|
-
redeployTargetSchema =
|
|
3961
|
-
repoFullName:
|
|
3962
|
-
prNumber:
|
|
3963
|
-
organizationId:
|
|
3964
|
-
githubRepositoryId:
|
|
3965
|
-
headSha:
|
|
3966
|
-
headRef:
|
|
4144
|
+
redeployTargetSchema = z17.object({
|
|
4145
|
+
repoFullName: z17.string().min(1),
|
|
4146
|
+
prNumber: z17.number().int().nonnegative(),
|
|
4147
|
+
organizationId: z17.string().min(1),
|
|
4148
|
+
githubRepositoryId: z17.number().int(),
|
|
4149
|
+
headSha: z17.string().min(1),
|
|
4150
|
+
headRef: z17.string().min(1)
|
|
3967
4151
|
});
|
|
3968
|
-
teardownTargetSchema =
|
|
3969
|
-
repoFullName:
|
|
3970
|
-
prNumber:
|
|
3971
|
-
organizationId:
|
|
3972
|
-
headSha:
|
|
4152
|
+
teardownTargetSchema = z17.object({
|
|
4153
|
+
repoFullName: z17.string().min(1),
|
|
4154
|
+
prNumber: z17.number().int().nonnegative(),
|
|
4155
|
+
organizationId: z17.string().min(1),
|
|
4156
|
+
headSha: z17.string().min(1).optional()
|
|
3973
4157
|
});
|
|
3974
|
-
previewJobSpecSchema =
|
|
3975
|
-
|
|
3976
|
-
|
|
3977
|
-
|
|
3978
|
-
mode:
|
|
4158
|
+
previewJobSpecSchema = z17.discriminatedUnion("mode", [
|
|
4159
|
+
z17.object({ mode: z17.literal("deploy"), target: deployTargetSchema }),
|
|
4160
|
+
z17.object({ mode: z17.literal("teardown"), target: teardownTargetSchema }),
|
|
4161
|
+
z17.object({
|
|
4162
|
+
mode: z17.literal("redeploy-app"),
|
|
3979
4163
|
target: redeployTargetSchema,
|
|
3980
|
-
namespace:
|
|
3981
|
-
appName:
|
|
3982
|
-
redeployMode:
|
|
4164
|
+
namespace: z17.string().min(1),
|
|
4165
|
+
appName: z17.string().min(1),
|
|
4166
|
+
redeployMode: z17.enum(["rebuild", "restart"])
|
|
3983
4167
|
})
|
|
3984
4168
|
]);
|
|
3985
4169
|
}
|
|
@@ -4004,29 +4188,29 @@ var init_previewkit_authorable_config = __esm({
|
|
|
4004
4188
|
});
|
|
4005
4189
|
|
|
4006
4190
|
// ../../packages/types/src/schemas/previewkit-redeploy-app.ts
|
|
4007
|
-
import { z as
|
|
4191
|
+
import { z as z18 } from "zod";
|
|
4008
4192
|
var RedeployPreviewkitAppInputSchema;
|
|
4009
4193
|
var init_previewkit_redeploy_app = __esm({
|
|
4010
4194
|
"../../packages/types/src/schemas/previewkit-redeploy-app.ts"() {
|
|
4011
4195
|
"use strict";
|
|
4012
4196
|
init_esm_shims();
|
|
4013
|
-
RedeployPreviewkitAppInputSchema =
|
|
4014
|
-
applicationId:
|
|
4015
|
-
environmentId:
|
|
4016
|
-
app:
|
|
4017
|
-
mode:
|
|
4197
|
+
RedeployPreviewkitAppInputSchema = z18.object({
|
|
4198
|
+
applicationId: z18.string().min(1),
|
|
4199
|
+
environmentId: z18.string().min(1),
|
|
4200
|
+
app: z18.string().min(1),
|
|
4201
|
+
mode: z18.enum(["rebuild", "restart"])
|
|
4018
4202
|
});
|
|
4019
4203
|
}
|
|
4020
4204
|
});
|
|
4021
4205
|
|
|
4022
4206
|
// ../../packages/types/src/schemas/previewkit-service-suggestion.ts
|
|
4023
|
-
import { z as
|
|
4207
|
+
import { z as z19 } from "zod";
|
|
4024
4208
|
var SuggestableServiceRecipeSchema, SuggestionAppRefSchema, SuggestServicesInputSchema, SuggestedServiceSchema, SuggestServicesResultSchema;
|
|
4025
4209
|
var init_previewkit_service_suggestion = __esm({
|
|
4026
4210
|
"../../packages/types/src/schemas/previewkit-service-suggestion.ts"() {
|
|
4027
4211
|
"use strict";
|
|
4028
4212
|
init_esm_shims();
|
|
4029
|
-
SuggestableServiceRecipeSchema =
|
|
4213
|
+
SuggestableServiceRecipeSchema = z19.enum([
|
|
4030
4214
|
"postgres",
|
|
4031
4215
|
"redis",
|
|
4032
4216
|
"valkey",
|
|
@@ -4035,144 +4219,133 @@ var init_previewkit_service_suggestion = __esm({
|
|
|
4035
4219
|
"temporal",
|
|
4036
4220
|
"docker-image"
|
|
4037
4221
|
]);
|
|
4038
|
-
SuggestionAppRefSchema =
|
|
4039
|
-
name:
|
|
4040
|
-
path:
|
|
4041
|
-
primary:
|
|
4222
|
+
SuggestionAppRefSchema = z19.object({
|
|
4223
|
+
name: z19.string(),
|
|
4224
|
+
path: z19.string(),
|
|
4225
|
+
primary: z19.boolean().optional()
|
|
4042
4226
|
});
|
|
4043
|
-
SuggestServicesInputSchema =
|
|
4044
|
-
applicationId:
|
|
4045
|
-
githubRepositoryId:
|
|
4046
|
-
apps:
|
|
4227
|
+
SuggestServicesInputSchema = z19.object({
|
|
4228
|
+
applicationId: z19.string(),
|
|
4229
|
+
githubRepositoryId: z19.number().int().positive().optional(),
|
|
4230
|
+
apps: z19.array(SuggestionAppRefSchema)
|
|
4047
4231
|
});
|
|
4048
|
-
SuggestedServiceSchema =
|
|
4232
|
+
SuggestedServiceSchema = z19.object({
|
|
4049
4233
|
recipe: SuggestableServiceRecipeSchema,
|
|
4050
|
-
name:
|
|
4051
|
-
version:
|
|
4052
|
-
confidence:
|
|
4053
|
-
evidence:
|
|
4234
|
+
name: z19.string(),
|
|
4235
|
+
version: z19.string().optional(),
|
|
4236
|
+
confidence: z19.enum(["high", "medium", "low"]),
|
|
4237
|
+
evidence: z19.array(z19.string())
|
|
4054
4238
|
});
|
|
4055
|
-
SuggestServicesResultSchema =
|
|
4056
|
-
status:
|
|
4057
|
-
reason:
|
|
4058
|
-
services:
|
|
4239
|
+
SuggestServicesResultSchema = z19.object({
|
|
4240
|
+
status: z19.enum(["ok", "unavailable"]),
|
|
4241
|
+
reason: z19.string().optional(),
|
|
4242
|
+
services: z19.array(SuggestedServiceSchema)
|
|
4059
4243
|
});
|
|
4060
4244
|
}
|
|
4061
4245
|
});
|
|
4062
4246
|
|
|
4063
4247
|
// ../../packages/types/src/schemas/previewkit-env-suggestion.ts
|
|
4064
|
-
import { z as
|
|
4248
|
+
import { z as z20 } from "zod";
|
|
4065
4249
|
var SuggestionServiceRefSchema, SuggestEnvVarsInputSchema, SuggestedEnvVarSchema, SuggestedEnvGroupSchema, SuggestEnvVarsResultSchema;
|
|
4066
4250
|
var init_previewkit_env_suggestion = __esm({
|
|
4067
4251
|
"../../packages/types/src/schemas/previewkit-env-suggestion.ts"() {
|
|
4068
4252
|
"use strict";
|
|
4069
4253
|
init_esm_shims();
|
|
4070
4254
|
init_previewkit_service_suggestion();
|
|
4071
|
-
SuggestionServiceRefSchema =
|
|
4072
|
-
name:
|
|
4255
|
+
SuggestionServiceRefSchema = z20.object({
|
|
4256
|
+
name: z20.string(),
|
|
4073
4257
|
recipe: SuggestableServiceRecipeSchema
|
|
4074
4258
|
});
|
|
4075
|
-
SuggestEnvVarsInputSchema =
|
|
4076
|
-
applicationId:
|
|
4077
|
-
githubRepositoryId:
|
|
4078
|
-
apps:
|
|
4079
|
-
services:
|
|
4259
|
+
SuggestEnvVarsInputSchema = z20.object({
|
|
4260
|
+
applicationId: z20.string(),
|
|
4261
|
+
githubRepositoryId: z20.number().int().positive().optional(),
|
|
4262
|
+
apps: z20.array(SuggestionAppRefSchema),
|
|
4263
|
+
services: z20.array(SuggestionServiceRefSchema)
|
|
4080
4264
|
});
|
|
4081
|
-
SuggestedEnvVarSchema =
|
|
4082
|
-
key:
|
|
4083
|
-
value:
|
|
4084
|
-
reference:
|
|
4085
|
-
sensitive:
|
|
4265
|
+
SuggestedEnvVarSchema = z20.object({
|
|
4266
|
+
key: z20.string(),
|
|
4267
|
+
value: z20.string().optional(),
|
|
4268
|
+
reference: z20.string().optional(),
|
|
4269
|
+
sensitive: z20.boolean(),
|
|
4086
4270
|
/**
|
|
4087
4271
|
* Whether the value must be present during the image BUILD (not just at
|
|
4088
4272
|
* runtime) - e.g. framework vars inlined into the client bundle
|
|
4089
4273
|
* (`NEXT_PUBLIC_*`, `VITE_*`) or values read by the build/install command.
|
|
4090
4274
|
* Maps to the row's "also inject at build time" switch. Omitted = runtime-only.
|
|
4091
4275
|
*/
|
|
4092
|
-
build_time:
|
|
4093
|
-
description:
|
|
4094
|
-
confidence:
|
|
4095
|
-
evidence:
|
|
4276
|
+
build_time: z20.boolean().optional(),
|
|
4277
|
+
description: z20.string().optional(),
|
|
4278
|
+
confidence: z20.enum(["high", "medium", "low"]),
|
|
4279
|
+
evidence: z20.array(z20.string())
|
|
4096
4280
|
});
|
|
4097
|
-
SuggestedEnvGroupSchema =
|
|
4098
|
-
name:
|
|
4099
|
-
vars:
|
|
4281
|
+
SuggestedEnvGroupSchema = z20.object({
|
|
4282
|
+
name: z20.string(),
|
|
4283
|
+
vars: z20.array(SuggestedEnvVarSchema)
|
|
4100
4284
|
});
|
|
4101
|
-
SuggestEnvVarsResultSchema =
|
|
4102
|
-
status:
|
|
4103
|
-
reason:
|
|
4104
|
-
apps:
|
|
4105
|
-
services:
|
|
4285
|
+
SuggestEnvVarsResultSchema = z20.object({
|
|
4286
|
+
status: z20.enum(["ok", "unavailable"]),
|
|
4287
|
+
reason: z20.string().optional(),
|
|
4288
|
+
apps: z20.array(SuggestedEnvGroupSchema),
|
|
4289
|
+
services: z20.array(SuggestedEnvGroupSchema)
|
|
4106
4290
|
});
|
|
4107
4291
|
}
|
|
4108
4292
|
});
|
|
4109
4293
|
|
|
4110
4294
|
// ../../packages/types/src/schemas/previewkit-diagnosis.ts
|
|
4111
|
-
import { z as
|
|
4112
|
-
var DiagnosePreviewkitDeployInputSchema, PreviewDiagnosisCategorySchema, PreviewDiagnosisActionSchema, PreviewDiagnosisFindingSchema
|
|
4295
|
+
import { z as z21 } from "zod";
|
|
4296
|
+
var DiagnosePreviewkitDeployInputSchema, PreviewDiagnosisCategorySchema, PreviewDiagnosisActionSchema, PreviewDiagnosisFindingSchema;
|
|
4113
4297
|
var init_previewkit_diagnosis = __esm({
|
|
4114
4298
|
"../../packages/types/src/schemas/previewkit-diagnosis.ts"() {
|
|
4115
4299
|
"use strict";
|
|
4116
4300
|
init_esm_shims();
|
|
4117
4301
|
init_previewkit_env_suggestion();
|
|
4118
|
-
DiagnosePreviewkitDeployInputSchema =
|
|
4119
|
-
applicationId:
|
|
4302
|
+
DiagnosePreviewkitDeployInputSchema = z21.object({
|
|
4303
|
+
applicationId: z21.string(),
|
|
4120
4304
|
/**
|
|
4121
4305
|
* Which preview environment to diagnose. Omitted defaults (in the service) to
|
|
4122
|
-
* 0 - the main-branch
|
|
4123
|
-
* is unchanged; MCP / per-PR callers pass the real PR number.
|
|
4306
|
+
* 0 - the main-branch preview; per-PR callers pass the real PR number.
|
|
4124
4307
|
*/
|
|
4125
|
-
prNumber:
|
|
4126
|
-
deployFingerprint:
|
|
4308
|
+
prNumber: z21.number().int().min(0).optional(),
|
|
4309
|
+
deployFingerprint: z21.string().optional()
|
|
4127
4310
|
});
|
|
4128
|
-
PreviewDiagnosisCategorySchema =
|
|
4129
|
-
PreviewDiagnosisActionSchema =
|
|
4130
|
-
PreviewDiagnosisFindingSchema =
|
|
4311
|
+
PreviewDiagnosisCategorySchema = z21.enum(["missing_env_var", "user_setup", "autonoma_error", "unknown"]);
|
|
4312
|
+
PreviewDiagnosisActionSchema = z21.enum(["edit_config", "edit_secrets", "redeploy", "contact_support"]);
|
|
4313
|
+
PreviewDiagnosisFindingSchema = z21.object({
|
|
4131
4314
|
category: PreviewDiagnosisCategorySchema,
|
|
4132
|
-
severity:
|
|
4133
|
-
title:
|
|
4134
|
-
explanation:
|
|
4135
|
-
fixSteps:
|
|
4136
|
-
appName:
|
|
4137
|
-
fieldPath:
|
|
4315
|
+
severity: z21.enum(["blocking", "warning", "info"]),
|
|
4316
|
+
title: z21.string(),
|
|
4317
|
+
explanation: z21.string(),
|
|
4318
|
+
fixSteps: z21.array(z21.string()),
|
|
4319
|
+
appName: z21.string().optional(),
|
|
4320
|
+
fieldPath: z21.string().optional(),
|
|
4138
4321
|
action: PreviewDiagnosisActionSchema.optional(),
|
|
4139
|
-
suggestedEnv:
|
|
4140
|
-
confidence:
|
|
4141
|
-
evidence:
|
|
4142
|
-
});
|
|
4143
|
-
DiagnosePreviewkitDeployResultSchema = z20.object({
|
|
4144
|
-
status: z20.enum(["ok", "unavailable"]),
|
|
4145
|
-
reason: z20.string().optional(),
|
|
4146
|
-
summary: z20.string().optional(),
|
|
4147
|
-
findings: z20.array(PreviewDiagnosisFindingSchema)
|
|
4148
|
-
});
|
|
4149
|
-
AiDiagnosisResultSchema = z20.object({
|
|
4150
|
-
summary: z20.string(),
|
|
4151
|
-
findings: z20.array(PreviewDiagnosisFindingSchema)
|
|
4322
|
+
suggestedEnv: z21.array(SuggestedEnvVarSchema).optional(),
|
|
4323
|
+
confidence: z21.enum(["high", "medium", "low"]),
|
|
4324
|
+
evidence: z21.array(z21.string())
|
|
4152
4325
|
});
|
|
4153
4326
|
}
|
|
4154
4327
|
});
|
|
4155
4328
|
|
|
4156
4329
|
// ../../packages/types/src/schemas/snapshot-dependency-pin.ts
|
|
4157
|
-
import { z as
|
|
4330
|
+
import { z as z22 } from "zod";
|
|
4158
4331
|
var snapshotDependencyShaMapSchema;
|
|
4159
4332
|
var init_snapshot_dependency_pin = __esm({
|
|
4160
4333
|
"../../packages/types/src/schemas/snapshot-dependency-pin.ts"() {
|
|
4161
4334
|
"use strict";
|
|
4162
4335
|
init_esm_shims();
|
|
4163
|
-
snapshotDependencyShaMapSchema =
|
|
4336
|
+
snapshotDependencyShaMapSchema = z22.record(z22.string(), z22.string());
|
|
4164
4337
|
}
|
|
4165
4338
|
});
|
|
4166
4339
|
|
|
4167
4340
|
// ../../packages/types/src/schemas/checkpoint-summary.ts
|
|
4168
|
-
import { z as
|
|
4341
|
+
import { z as z23 } from "zod";
|
|
4169
4342
|
var checkpointToneSchema, checkpointExecutionStateSchema, checkpointTestCountsSchema, checkpointAnalysisSummarySchema, checkpointPresentationSummarySchema;
|
|
4170
4343
|
var init_checkpoint_summary = __esm({
|
|
4171
4344
|
"../../packages/types/src/schemas/checkpoint-summary.ts"() {
|
|
4172
4345
|
"use strict";
|
|
4173
4346
|
init_esm_shims();
|
|
4174
|
-
checkpointToneSchema =
|
|
4175
|
-
checkpointExecutionStateSchema =
|
|
4347
|
+
checkpointToneSchema = z23.enum(["success", "critical", "warning", "neutral"]);
|
|
4348
|
+
checkpointExecutionStateSchema = z23.enum([
|
|
4176
4349
|
"not_started",
|
|
4177
4350
|
"running",
|
|
4178
4351
|
"stale",
|
|
@@ -4181,34 +4354,34 @@ var init_checkpoint_summary = __esm({
|
|
|
4181
4354
|
"pipeline_failed",
|
|
4182
4355
|
"unknown"
|
|
4183
4356
|
]);
|
|
4184
|
-
checkpointTestCountsSchema =
|
|
4185
|
-
assigned:
|
|
4186
|
-
run:
|
|
4187
|
-
passed:
|
|
4188
|
-
failed:
|
|
4357
|
+
checkpointTestCountsSchema = z23.object({
|
|
4358
|
+
assigned: z23.number(),
|
|
4359
|
+
run: z23.number(),
|
|
4360
|
+
passed: z23.number(),
|
|
4361
|
+
failed: z23.number(),
|
|
4189
4362
|
// Tests that never ran because their scenario setup failed.
|
|
4190
|
-
setupFailed:
|
|
4191
|
-
running:
|
|
4192
|
-
notRun:
|
|
4363
|
+
setupFailed: z23.number(),
|
|
4364
|
+
running: z23.number(),
|
|
4365
|
+
notRun: z23.number()
|
|
4193
4366
|
});
|
|
4194
|
-
checkpointAnalysisSummarySchema =
|
|
4367
|
+
checkpointAnalysisSummarySchema = z23.object({
|
|
4195
4368
|
// The AnalysisJob lifecycle. Mirrors the `AnalysisJobStatus` db enum (types cannot import it).
|
|
4196
|
-
jobStatus:
|
|
4369
|
+
jobStatus: z23.enum(["running", "completed", "failed"]),
|
|
4197
4370
|
// Client-bug findings - the only plane that counts against the PR (turns the checkpoint red).
|
|
4198
|
-
bugCount:
|
|
4371
|
+
bugCount: z23.number().int().nonnegative(),
|
|
4199
4372
|
// Findings that passed on the app-health plane.
|
|
4200
|
-
passedCount:
|
|
4373
|
+
passedCount: z23.number().int().nonnegative(),
|
|
4201
4374
|
// Coverage-plane findings (engine_artifact / environment_failure / scenario_issue / delete) - never a
|
|
4202
4375
|
// failure; surfaced as "couldn't confirm".
|
|
4203
|
-
coverageCount:
|
|
4376
|
+
coverageCount: z23.number().int().nonnegative()
|
|
4204
4377
|
});
|
|
4205
|
-
checkpointPresentationSummarySchema =
|
|
4378
|
+
checkpointPresentationSummarySchema = z23.object({
|
|
4206
4379
|
tone: checkpointToneSchema,
|
|
4207
|
-
label:
|
|
4208
|
-
reason:
|
|
4380
|
+
label: z23.string(),
|
|
4381
|
+
reason: z23.string().optional(),
|
|
4209
4382
|
executionState: checkpointExecutionStateSchema,
|
|
4210
4383
|
testCounts: checkpointTestCountsSchema,
|
|
4211
|
-
suiteChangeCount:
|
|
4384
|
+
suiteChangeCount: z23.number(),
|
|
4212
4385
|
// Set only for authoritative-analysis snapshots; absent for legacy diffs/shadow snapshots.
|
|
4213
4386
|
analysis: checkpointAnalysisSummarySchema.optional()
|
|
4214
4387
|
});
|
|
@@ -4216,65 +4389,65 @@ var init_checkpoint_summary = __esm({
|
|
|
4216
4389
|
});
|
|
4217
4390
|
|
|
4218
4391
|
// ../../packages/types/src/schemas/snapshot-report.ts
|
|
4219
|
-
import { z as
|
|
4392
|
+
import { z as z24 } from "zod";
|
|
4220
4393
|
var reportHealthSchema, reportTestStatusSchema, reportCommitFileSchema, snapshotReportTriggerSchema, snapshotReportTestResultSchema, snapshotReportResultsSchema, snapshotReportHealthCountsSchema, snapshotReportSchema;
|
|
4221
4394
|
var init_snapshot_report = __esm({
|
|
4222
4395
|
"../../packages/types/src/schemas/snapshot-report.ts"() {
|
|
4223
4396
|
"use strict";
|
|
4224
4397
|
init_esm_shims();
|
|
4225
4398
|
init_checkpoint_summary();
|
|
4226
|
-
reportHealthSchema =
|
|
4227
|
-
reportTestStatusSchema =
|
|
4228
|
-
reportCommitFileSchema =
|
|
4229
|
-
filename:
|
|
4230
|
-
status:
|
|
4231
|
-
additions:
|
|
4232
|
-
deletions:
|
|
4399
|
+
reportHealthSchema = z24.enum(["healthy", "critical", "running", "unknown"]);
|
|
4400
|
+
reportTestStatusSchema = z24.enum(["passed", "failed", "setup_failed", "running", "pending"]);
|
|
4401
|
+
reportCommitFileSchema = z24.object({
|
|
4402
|
+
filename: z24.string(),
|
|
4403
|
+
status: z24.string(),
|
|
4404
|
+
additions: z24.number(),
|
|
4405
|
+
deletions: z24.number()
|
|
4233
4406
|
});
|
|
4234
|
-
snapshotReportTriggerSchema =
|
|
4235
|
-
headSha:
|
|
4236
|
-
baseSha:
|
|
4237
|
-
source:
|
|
4238
|
-
createdAt:
|
|
4239
|
-
commit:
|
|
4240
|
-
filesChanged:
|
|
4241
|
-
filesChangedTruncated:
|
|
4407
|
+
snapshotReportTriggerSchema = z24.object({
|
|
4408
|
+
headSha: z24.string().optional(),
|
|
4409
|
+
baseSha: z24.string().optional(),
|
|
4410
|
+
source: z24.string(),
|
|
4411
|
+
createdAt: z24.date(),
|
|
4412
|
+
commit: z24.object({ message: z24.string(), authorLogin: z24.string().optional() }).optional(),
|
|
4413
|
+
filesChanged: z24.array(reportCommitFileSchema),
|
|
4414
|
+
filesChangedTruncated: z24.boolean()
|
|
4242
4415
|
});
|
|
4243
|
-
snapshotReportTestResultSchema =
|
|
4244
|
-
testCaseId:
|
|
4245
|
-
name:
|
|
4246
|
-
slug:
|
|
4416
|
+
snapshotReportTestResultSchema = z24.object({
|
|
4417
|
+
testCaseId: z24.string(),
|
|
4418
|
+
name: z24.string(),
|
|
4419
|
+
slug: z24.string(),
|
|
4247
4420
|
status: reportTestStatusSchema,
|
|
4248
|
-
runId:
|
|
4249
|
-
durationMs:
|
|
4421
|
+
runId: z24.string().optional(),
|
|
4422
|
+
durationMs: z24.number().optional()
|
|
4250
4423
|
});
|
|
4251
|
-
snapshotReportResultsSchema =
|
|
4252
|
-
durationMs:
|
|
4253
|
-
passed:
|
|
4254
|
-
failed:
|
|
4255
|
-
setupFailed:
|
|
4256
|
-
pending:
|
|
4257
|
-
running:
|
|
4258
|
-
total:
|
|
4259
|
-
tests:
|
|
4424
|
+
snapshotReportResultsSchema = z24.object({
|
|
4425
|
+
durationMs: z24.number().optional(),
|
|
4426
|
+
passed: z24.number(),
|
|
4427
|
+
failed: z24.number(),
|
|
4428
|
+
setupFailed: z24.number(),
|
|
4429
|
+
pending: z24.number(),
|
|
4430
|
+
running: z24.number(),
|
|
4431
|
+
total: z24.number(),
|
|
4432
|
+
tests: z24.array(snapshotReportTestResultSchema)
|
|
4260
4433
|
});
|
|
4261
|
-
snapshotReportHealthCountsSchema =
|
|
4262
|
-
failing:
|
|
4263
|
-
passing:
|
|
4264
|
-
running:
|
|
4265
|
-
setupFailed:
|
|
4266
|
-
notAffected:
|
|
4267
|
-
totalTests:
|
|
4434
|
+
snapshotReportHealthCountsSchema = z24.object({
|
|
4435
|
+
failing: z24.number(),
|
|
4436
|
+
passing: z24.number(),
|
|
4437
|
+
running: z24.number(),
|
|
4438
|
+
setupFailed: z24.number(),
|
|
4439
|
+
notAffected: z24.number(),
|
|
4440
|
+
totalTests: z24.number()
|
|
4268
4441
|
});
|
|
4269
|
-
snapshotReportSchema =
|
|
4270
|
-
snapshot:
|
|
4271
|
-
id:
|
|
4272
|
-
status:
|
|
4273
|
-
source:
|
|
4274
|
-
headSha:
|
|
4275
|
-
baseSha:
|
|
4276
|
-
createdAt:
|
|
4277
|
-
branch:
|
|
4442
|
+
snapshotReportSchema = z24.object({
|
|
4443
|
+
snapshot: z24.object({
|
|
4444
|
+
id: z24.string(),
|
|
4445
|
+
status: z24.string(),
|
|
4446
|
+
source: z24.string(),
|
|
4447
|
+
headSha: z24.string().optional(),
|
|
4448
|
+
baseSha: z24.string().optional(),
|
|
4449
|
+
createdAt: z24.date(),
|
|
4450
|
+
branch: z24.object({ id: z24.string(), name: z24.string(), prNumber: z24.number().optional() })
|
|
4278
4451
|
}),
|
|
4279
4452
|
trigger: snapshotReportTriggerSchema,
|
|
4280
4453
|
results: snapshotReportResultsSchema,
|
|
@@ -4286,35 +4459,35 @@ var init_snapshot_report = __esm({
|
|
|
4286
4459
|
});
|
|
4287
4460
|
|
|
4288
4461
|
// ../../packages/types/src/schemas/pr-pipeline-status.ts
|
|
4289
|
-
import { z as
|
|
4462
|
+
import { z as z25 } from "zod";
|
|
4290
4463
|
var prPipelineStatusSchema;
|
|
4291
4464
|
var init_pr_pipeline_status = __esm({
|
|
4292
4465
|
"../../packages/types/src/schemas/pr-pipeline-status.ts"() {
|
|
4293
4466
|
"use strict";
|
|
4294
4467
|
init_esm_shims();
|
|
4295
4468
|
init_checkpoint_summary();
|
|
4296
|
-
prPipelineStatusSchema =
|
|
4297
|
-
|
|
4298
|
-
|
|
4299
|
-
|
|
4300
|
-
|
|
4301
|
-
|
|
4302
|
-
|
|
4303
|
-
|
|
4469
|
+
prPipelineStatusSchema = z25.discriminatedUnion("kind", [
|
|
4470
|
+
z25.object({ kind: z25.literal("checkpoint"), summary: checkpointPresentationSummarySchema }),
|
|
4471
|
+
z25.object({ kind: z25.literal("building") }),
|
|
4472
|
+
z25.object({ kind: z25.literal("pending_checks") }),
|
|
4473
|
+
z25.object({ kind: z25.literal("analyzing") }),
|
|
4474
|
+
z25.object({ kind: z25.literal("analysis_failed") }),
|
|
4475
|
+
z25.object({ kind: z25.literal("build_failed") }),
|
|
4476
|
+
z25.object({ kind: z25.literal("none") })
|
|
4304
4477
|
]);
|
|
4305
4478
|
}
|
|
4306
4479
|
});
|
|
4307
4480
|
|
|
4308
4481
|
// ../../packages/types/src/schemas/step-output.ts
|
|
4309
|
-
import { z as
|
|
4482
|
+
import { z as z26 } from "zod";
|
|
4310
4483
|
var pointSchema2, stepOutputDataSchema;
|
|
4311
4484
|
var init_step_output = __esm({
|
|
4312
4485
|
"../../packages/types/src/schemas/step-output.ts"() {
|
|
4313
4486
|
"use strict";
|
|
4314
4487
|
init_esm_shims();
|
|
4315
|
-
pointSchema2 =
|
|
4316
|
-
stepOutputDataSchema =
|
|
4317
|
-
outcome:
|
|
4488
|
+
pointSchema2 = z26.object({ x: z26.number(), y: z26.number() });
|
|
4489
|
+
stepOutputDataSchema = z26.object({
|
|
4490
|
+
outcome: z26.string().optional(),
|
|
4318
4491
|
point: pointSchema2.optional(),
|
|
4319
4492
|
startPoint: pointSchema2.optional(),
|
|
4320
4493
|
endPoint: pointSchema2.optional()
|
|
@@ -4323,7 +4496,7 @@ var init_step_output = __esm({
|
|
|
4323
4496
|
});
|
|
4324
4497
|
|
|
4325
4498
|
// ../../packages/types/src/schemas/activation-triggers.ts
|
|
4326
|
-
import { z as
|
|
4499
|
+
import { z as z27 } from "zod";
|
|
4327
4500
|
var MAX_LABEL_LENGTH, CONTROL_CHARS, AnalysisTriggerLabelSchema, TriggerConfigSchema;
|
|
4328
4501
|
var init_activation_triggers = __esm({
|
|
4329
4502
|
"../../packages/types/src/schemas/activation-triggers.ts"() {
|
|
@@ -4331,10 +4504,10 @@ var init_activation_triggers = __esm({
|
|
|
4331
4504
|
init_esm_shims();
|
|
4332
4505
|
MAX_LABEL_LENGTH = 50;
|
|
4333
4506
|
CONTROL_CHARS = /[\u0000-\u001F\u007F]/;
|
|
4334
|
-
AnalysisTriggerLabelSchema =
|
|
4335
|
-
TriggerConfigSchema =
|
|
4507
|
+
AnalysisTriggerLabelSchema = z27.string().trim().min(1, "Label cannot be empty").max(MAX_LABEL_LENGTH, `Label must be at most ${MAX_LABEL_LENGTH} characters`).refine((value) => !CONTROL_CHARS.test(value), "Label cannot contain control characters");
|
|
4508
|
+
TriggerConfigSchema = z27.object({
|
|
4336
4509
|
/** Whether marking a PR ready-for-review automatically starts an analysis run. */
|
|
4337
|
-
autoRunOnReadyForReview:
|
|
4510
|
+
autoRunOnReadyForReview: z27.boolean(),
|
|
4338
4511
|
/** The PR label whose addition starts an analysis run. */
|
|
4339
4512
|
analysisTriggerLabel: AnalysisTriggerLabelSchema
|
|
4340
4513
|
});
|
|
@@ -4342,67 +4515,67 @@ var init_activation_triggers = __esm({
|
|
|
4342
4515
|
});
|
|
4343
4516
|
|
|
4344
4517
|
// ../../packages/types/src/schemas/suite-health.ts
|
|
4345
|
-
import { z as
|
|
4518
|
+
import { z as z28 } from "zod";
|
|
4346
4519
|
var SUITE_HEALTH_LEVELS, suiteHealthLevelSchema, suiteHealthDriverSchema, suiteHealthGateSchema, suiteHealthEvidenceSchema, suiteHealthBreakdownSchema, suiteHealthSchema;
|
|
4347
4520
|
var init_suite_health = __esm({
|
|
4348
4521
|
"../../packages/types/src/schemas/suite-health.ts"() {
|
|
4349
4522
|
"use strict";
|
|
4350
4523
|
init_esm_shims();
|
|
4351
4524
|
SUITE_HEALTH_LEVELS = ["degraded", "at_risk", "calibrating", "steady", "proven"];
|
|
4352
|
-
suiteHealthLevelSchema =
|
|
4353
|
-
suiteHealthDriverSchema =
|
|
4354
|
-
suiteHealthGateSchema =
|
|
4355
|
-
suiteHealthEvidenceSchema =
|
|
4525
|
+
suiteHealthLevelSchema = z28.enum(SUITE_HEALTH_LEVELS);
|
|
4526
|
+
suiteHealthDriverSchema = z28.enum(["environment", "scenario", "plan", "engine", "balanced", "none"]);
|
|
4527
|
+
suiteHealthGateSchema = z28.enum(["runs", "pull_requests", "age", "stale_issues", "no_triage"]);
|
|
4528
|
+
suiteHealthEvidenceSchema = z28.object({
|
|
4356
4529
|
/** Analysis runs in the window. A run that selected no tests is not one. */
|
|
4357
|
-
runs:
|
|
4530
|
+
runs: z28.number().int(),
|
|
4358
4531
|
/** Distinct branches those runs covered. */
|
|
4359
|
-
pullRequests:
|
|
4532
|
+
pullRequests: z28.number().int(),
|
|
4360
4533
|
/** Tests the agent re-planned and re-ran, that then passed. */
|
|
4361
|
-
selfHeals:
|
|
4534
|
+
selfHeals: z28.number().int(),
|
|
4362
4535
|
/** Tests the agent re-planned and re-ran at all - the denominator of the self-heal rate. */
|
|
4363
|
-
selfHealAttempts:
|
|
4536
|
+
selfHealAttempts: z28.number().int(),
|
|
4364
4537
|
/** Total findings in the window - the denominator of the trust rate. */
|
|
4365
|
-
findings:
|
|
4538
|
+
findings: z28.number().int(),
|
|
4366
4539
|
/** Whole days since the app's first analysis run of any pipeline. */
|
|
4367
|
-
ageDays:
|
|
4540
|
+
ageDays: z28.number().int(),
|
|
4368
4541
|
/** Whole days since its most recent run. Drives the inactivity decay. */
|
|
4369
|
-
daysSinceLastRun:
|
|
4542
|
+
daysSinceLastRun: z28.number().int()
|
|
4370
4543
|
});
|
|
4371
|
-
suiteHealthBreakdownSchema =
|
|
4372
|
-
passed:
|
|
4373
|
-
clientBug:
|
|
4374
|
-
environmentFailure:
|
|
4375
|
-
scenarioIssue:
|
|
4376
|
-
planMismatch:
|
|
4377
|
-
engineArtifact:
|
|
4378
|
-
invalidTest:
|
|
4544
|
+
suiteHealthBreakdownSchema = z28.object({
|
|
4545
|
+
passed: z28.number().int(),
|
|
4546
|
+
clientBug: z28.number().int(),
|
|
4547
|
+
environmentFailure: z28.number().int(),
|
|
4548
|
+
scenarioIssue: z28.number().int(),
|
|
4549
|
+
planMismatch: z28.number().int(),
|
|
4550
|
+
engineArtifact: z28.number().int(),
|
|
4551
|
+
invalidTest: z28.number().int()
|
|
4379
4552
|
});
|
|
4380
|
-
suiteHealthSchema =
|
|
4553
|
+
suiteHealthSchema = z28.object({
|
|
4381
4554
|
level: suiteHealthLevelSchema,
|
|
4382
4555
|
/** 1-5. Always `suiteHealthRank(level)`; sent so clients never re-derive it. */
|
|
4383
|
-
rank:
|
|
4556
|
+
rank: z28.number().int(),
|
|
4384
4557
|
/** 0-100, after modifiers. Display-only - the level is what surfaces read. */
|
|
4385
|
-
score:
|
|
4558
|
+
score: z28.number(),
|
|
4386
4559
|
/**
|
|
4387
4560
|
* `(passed + client_bug) / findings`, 0-100, before modifiers. The headline fact: of the tests the agent
|
|
4388
4561
|
* investigated, how many produced a verdict you can act on.
|
|
4389
4562
|
*/
|
|
4390
|
-
trust:
|
|
4563
|
+
trust: z28.number(),
|
|
4391
4564
|
evidence: suiteHealthEvidenceSchema,
|
|
4392
4565
|
breakdown: suiteHealthBreakdownSchema,
|
|
4393
4566
|
driver: suiteHealthDriverSchema,
|
|
4394
4567
|
/** Open issues older than a week on a branch that is still live. What DEGRADED is actually made of. */
|
|
4395
|
-
staleIssues:
|
|
4568
|
+
staleIssues: z28.number().int(),
|
|
4396
4569
|
/** The gate holding the level down, if the score alone would have placed it higher. */
|
|
4397
4570
|
gatedBy: suiteHealthGateSchema.optional(),
|
|
4398
4571
|
/** False before the first analysis run ever - the meter shows "waiting for your first pull request". */
|
|
4399
|
-
hasEverRun:
|
|
4572
|
+
hasEverRun: z28.boolean()
|
|
4400
4573
|
});
|
|
4401
4574
|
}
|
|
4402
4575
|
});
|
|
4403
4576
|
|
|
4404
4577
|
// ../../packages/types/src/schemas/suite-health-fix-plan.ts
|
|
4405
|
-
import { z as
|
|
4578
|
+
import { z as z29 } from "zod";
|
|
4406
4579
|
var suiteHealthFixKindSchema, suiteHealthFixSeveritySchema, suiteHealthFixIssueSchema, suiteHealthFixBranchStateSchema, suiteHealthFixBranchSchema, suiteHealthFixClusterSchema, suiteHealthFixPlanSchema;
|
|
4407
4580
|
var init_suite_health_fix_plan = __esm({
|
|
4408
4581
|
"../../packages/types/src/schemas/suite-health-fix-plan.ts"() {
|
|
@@ -4411,70 +4584,70 @@ var init_suite_health_fix_plan = __esm({
|
|
|
4411
4584
|
init_analysis();
|
|
4412
4585
|
suiteHealthFixKindSchema = analysisIssueKindSchema;
|
|
4413
4586
|
suiteHealthFixSeveritySchema = analysisIssueSeveritySchema;
|
|
4414
|
-
suiteHealthFixIssueSchema =
|
|
4415
|
-
id:
|
|
4587
|
+
suiteHealthFixIssueSchema = z29.object({
|
|
4588
|
+
id: z29.string(),
|
|
4416
4589
|
kind: suiteHealthFixKindSchema,
|
|
4417
4590
|
severity: suiteHealthFixSeveritySchema,
|
|
4418
|
-
title:
|
|
4419
|
-
ageDays:
|
|
4591
|
+
title: z29.string(),
|
|
4592
|
+
ageDays: z29.number().int()
|
|
4420
4593
|
});
|
|
4421
|
-
suiteHealthFixBranchStateSchema =
|
|
4422
|
-
suiteHealthFixBranchSchema =
|
|
4423
|
-
branchId:
|
|
4424
|
-
branchName:
|
|
4594
|
+
suiteHealthFixBranchStateSchema = z29.enum(["open", "merged", "closed", "main"]);
|
|
4595
|
+
suiteHealthFixBranchSchema = z29.object({
|
|
4596
|
+
branchId: z29.string(),
|
|
4597
|
+
branchName: z29.string(),
|
|
4425
4598
|
state: suiteHealthFixBranchStateSchema,
|
|
4426
|
-
prNumber:
|
|
4427
|
-
prTitle:
|
|
4428
|
-
prUrl:
|
|
4599
|
+
prNumber: z29.number().int().optional(),
|
|
4600
|
+
prTitle: z29.string().optional(),
|
|
4601
|
+
prUrl: z29.string().optional(),
|
|
4429
4602
|
/** The findings shown. Capped - `issueCount` is the true total. */
|
|
4430
|
-
issues:
|
|
4431
|
-
issueCount:
|
|
4603
|
+
issues: z29.array(suiteHealthFixIssueSchema),
|
|
4604
|
+
issueCount: z29.number().int(),
|
|
4432
4605
|
/** Kind tally over EVERY finding on the branch, not just the shown ones - those two routinely differ. */
|
|
4433
|
-
byKind:
|
|
4434
|
-
bug:
|
|
4435
|
-
environment:
|
|
4436
|
-
scenario:
|
|
4606
|
+
byKind: z29.object({
|
|
4607
|
+
bug: z29.number().int(),
|
|
4608
|
+
environment: z29.number().int(),
|
|
4609
|
+
scenario: z29.number().int()
|
|
4437
4610
|
}),
|
|
4438
4611
|
/** Age of the oldest unresolved finding on this branch, in whole days. */
|
|
4439
|
-
oldestAgeDays:
|
|
4612
|
+
oldestAgeDays: z29.number().int()
|
|
4440
4613
|
});
|
|
4441
|
-
suiteHealthFixClusterSchema =
|
|
4442
|
-
title:
|
|
4614
|
+
suiteHealthFixClusterSchema = z29.object({
|
|
4615
|
+
title: z29.string(),
|
|
4443
4616
|
kind: suiteHealthFixKindSchema,
|
|
4444
4617
|
/** Distinct branches carrying it. */
|
|
4445
|
-
branches:
|
|
4618
|
+
branches: z29.number().int(),
|
|
4446
4619
|
/** How many of those are still-open pull requests - the ones a fix unblocks today. */
|
|
4447
|
-
openBranches:
|
|
4448
|
-
findings:
|
|
4620
|
+
openBranches: z29.number().int(),
|
|
4621
|
+
findings: z29.number().int()
|
|
4449
4622
|
});
|
|
4450
|
-
suiteHealthFixPlanSchema =
|
|
4623
|
+
suiteHealthFixPlanSchema = z29.object({
|
|
4451
4624
|
/**
|
|
4452
4625
|
* `owner/repo`, which every MCP tool is keyed by. Absent when it cannot be resolved without asking GitHub,
|
|
4453
4626
|
* in which case the prompt tells the agent to read the git remote instead - which is what the MCP's own
|
|
4454
4627
|
* instructions tell it to do anyway.
|
|
4455
4628
|
*/
|
|
4456
|
-
repoFullName:
|
|
4629
|
+
repoFullName: z29.string().optional(),
|
|
4457
4630
|
/** Unresolved findings across every branch. */
|
|
4458
|
-
totalIssues:
|
|
4459
|
-
byKind:
|
|
4460
|
-
bug:
|
|
4461
|
-
environment:
|
|
4462
|
-
scenario:
|
|
4631
|
+
totalIssues: z29.number().int(),
|
|
4632
|
+
byKind: z29.object({
|
|
4633
|
+
bug: z29.number().int(),
|
|
4634
|
+
environment: z29.number().int(),
|
|
4635
|
+
scenario: z29.number().int()
|
|
4463
4636
|
}),
|
|
4464
4637
|
/** Age of the oldest unresolved finding anywhere on the app, in whole days. */
|
|
4465
|
-
oldestAgeDays:
|
|
4638
|
+
oldestAgeDays: z29.number().int(),
|
|
4466
4639
|
/** True when the scan hit its cap, so `totalIssues` is a floor rather than the count. Never hide this. */
|
|
4467
|
-
truncated:
|
|
4640
|
+
truncated: z29.boolean(),
|
|
4468
4641
|
/** Repeated findings, most-shared first. Empty when nothing repeats. */
|
|
4469
|
-
clusters:
|
|
4642
|
+
clusters: z29.array(suiteHealthFixClusterSchema),
|
|
4470
4643
|
/** The prompt the user copies. Authored from the rows above. */
|
|
4471
|
-
prompt:
|
|
4644
|
+
prompt: z29.string()
|
|
4472
4645
|
});
|
|
4473
4646
|
}
|
|
4474
4647
|
});
|
|
4475
4648
|
|
|
4476
4649
|
// ../../packages/types/src/schemas/index.ts
|
|
4477
|
-
import { z as
|
|
4650
|
+
import { z as z30 } from "zod";
|
|
4478
4651
|
var PlatformSchema, TestStatusSchema;
|
|
4479
4652
|
var init_schemas = __esm({
|
|
4480
4653
|
"../../packages/types/src/schemas/index.ts"() {
|
|
@@ -4488,9 +4661,11 @@ var init_schemas = __esm({
|
|
|
4488
4661
|
init_evidence_tokens();
|
|
4489
4662
|
init_generation();
|
|
4490
4663
|
init_api_key();
|
|
4664
|
+
init_organization();
|
|
4491
4665
|
init_secrets();
|
|
4492
4666
|
init_previewkit_builtins();
|
|
4493
4667
|
init_previewkit_config();
|
|
4668
|
+
init_previewkit_config_rows();
|
|
4494
4669
|
init_previewkit_job_spec();
|
|
4495
4670
|
init_previewkit_node_build();
|
|
4496
4671
|
init_previewkit_node_pm();
|
|
@@ -4510,8 +4685,8 @@ var init_schemas = __esm({
|
|
|
4510
4685
|
init_activation_triggers();
|
|
4511
4686
|
init_suite_health();
|
|
4512
4687
|
init_suite_health_fix_plan();
|
|
4513
|
-
PlatformSchema =
|
|
4514
|
-
TestStatusSchema =
|
|
4688
|
+
PlatformSchema = z30.enum(["web", "ios", "android"]);
|
|
4689
|
+
TestStatusSchema = z30.enum(["pending", "running", "passed", "failed", "cancelled"]);
|
|
4515
4690
|
}
|
|
4516
4691
|
});
|
|
4517
4692
|
|
|
@@ -4546,16 +4721,16 @@ var init_billing = __esm({
|
|
|
4546
4721
|
});
|
|
4547
4722
|
|
|
4548
4723
|
// ../../packages/types/src/schemas/billing.ts
|
|
4549
|
-
import { z as
|
|
4724
|
+
import { z as z31 } from "zod";
|
|
4550
4725
|
var BillingTransactionSchema, BillingStatusSchema, CreateCheckoutInputSchema, AutoTopUpInputSchema, RunCompletedSchema;
|
|
4551
4726
|
var init_billing2 = __esm({
|
|
4552
4727
|
"../../packages/types/src/schemas/billing.ts"() {
|
|
4553
4728
|
"use strict";
|
|
4554
4729
|
init_esm_shims();
|
|
4555
4730
|
init_billing();
|
|
4556
|
-
BillingTransactionSchema =
|
|
4557
|
-
id:
|
|
4558
|
-
type:
|
|
4731
|
+
BillingTransactionSchema = z31.object({
|
|
4732
|
+
id: z31.string(),
|
|
4733
|
+
type: z31.enum([
|
|
4559
4734
|
"SUBSCRIPTION_GRANT",
|
|
4560
4735
|
"SUBSCRIPTION_RESET",
|
|
4561
4736
|
"TOPUP_PURCHASE",
|
|
@@ -4564,70 +4739,70 @@ var init_billing2 = __esm({
|
|
|
4564
4739
|
"GENERATION_REFUND",
|
|
4565
4740
|
"RUN_CONSUMPTION"
|
|
4566
4741
|
]),
|
|
4567
|
-
amount:
|
|
4568
|
-
balanceAfter:
|
|
4569
|
-
generationId:
|
|
4570
|
-
runId:
|
|
4571
|
-
stripePaymentIntentId:
|
|
4572
|
-
stripeInvoiceId:
|
|
4573
|
-
stripeRefundId:
|
|
4574
|
-
createdAt:
|
|
4742
|
+
amount: z31.number(),
|
|
4743
|
+
balanceAfter: z31.number(),
|
|
4744
|
+
generationId: z31.string().optional(),
|
|
4745
|
+
runId: z31.string().optional(),
|
|
4746
|
+
stripePaymentIntentId: z31.string().optional(),
|
|
4747
|
+
stripeInvoiceId: z31.string().optional(),
|
|
4748
|
+
stripeRefundId: z31.string().optional(),
|
|
4749
|
+
createdAt: z31.date()
|
|
4575
4750
|
});
|
|
4576
|
-
BillingStatusSchema =
|
|
4577
|
-
creditBalance:
|
|
4578
|
-
subscriptionCreditBalance:
|
|
4579
|
-
topupCreditBalance:
|
|
4580
|
-
subscriptionStatus:
|
|
4581
|
-
currentPeriodEnd:
|
|
4582
|
-
cancelAtPeriodEnd:
|
|
4583
|
-
gracePeriodEndsAt:
|
|
4584
|
-
autoTopUpEnabled:
|
|
4585
|
-
autoTopUpThreshold:
|
|
4586
|
-
transactions:
|
|
4751
|
+
BillingStatusSchema = z31.object({
|
|
4752
|
+
creditBalance: z31.number(),
|
|
4753
|
+
subscriptionCreditBalance: z31.number(),
|
|
4754
|
+
topupCreditBalance: z31.number(),
|
|
4755
|
+
subscriptionStatus: z31.enum(["active", "canceled", "past_due", "unpaid", "trialing", "paused", "incomplete", "incomplete_expired"]).optional(),
|
|
4756
|
+
currentPeriodEnd: z31.date().optional(),
|
|
4757
|
+
cancelAtPeriodEnd: z31.boolean(),
|
|
4758
|
+
gracePeriodEndsAt: z31.date().optional(),
|
|
4759
|
+
autoTopUpEnabled: z31.boolean(),
|
|
4760
|
+
autoTopUpThreshold: z31.number(),
|
|
4761
|
+
transactions: z31.array(BillingTransactionSchema)
|
|
4587
4762
|
});
|
|
4588
|
-
CreateCheckoutInputSchema =
|
|
4589
|
-
type:
|
|
4763
|
+
CreateCheckoutInputSchema = z31.object({
|
|
4764
|
+
type: z31.enum([BILLING_CHECKOUT_TYPES.SUBSCRIPTION, BILLING_CHECKOUT_TYPES.TOPUP])
|
|
4590
4765
|
});
|
|
4591
|
-
AutoTopUpInputSchema =
|
|
4592
|
-
enabled:
|
|
4593
|
-
threshold:
|
|
4766
|
+
AutoTopUpInputSchema = z31.object({
|
|
4767
|
+
enabled: z31.boolean(),
|
|
4768
|
+
threshold: z31.number().int().min(0)
|
|
4594
4769
|
});
|
|
4595
|
-
RunCompletedSchema =
|
|
4596
|
-
generationId:
|
|
4770
|
+
RunCompletedSchema = z31.object({
|
|
4771
|
+
generationId: z31.string()
|
|
4597
4772
|
});
|
|
4598
4773
|
}
|
|
4599
4774
|
});
|
|
4600
4775
|
|
|
4601
4776
|
// ../../packages/types/src/schemas/github.ts
|
|
4602
|
-
import { z as
|
|
4777
|
+
import { z as z32 } from "zod";
|
|
4603
4778
|
var GitHubInstallationStatusSchema, GithubInstallationSchema;
|
|
4604
4779
|
var init_github = __esm({
|
|
4605
4780
|
"../../packages/types/src/schemas/github.ts"() {
|
|
4606
4781
|
"use strict";
|
|
4607
4782
|
init_esm_shims();
|
|
4608
|
-
GitHubInstallationStatusSchema =
|
|
4609
|
-
GithubInstallationSchema =
|
|
4610
|
-
id:
|
|
4611
|
-
installationId:
|
|
4612
|
-
organizationId:
|
|
4613
|
-
accountLogin:
|
|
4614
|
-
accountId:
|
|
4615
|
-
accountType:
|
|
4783
|
+
GitHubInstallationStatusSchema = z32.enum(["active", "suspended", "deleted"]);
|
|
4784
|
+
GithubInstallationSchema = z32.object({
|
|
4785
|
+
id: z32.string(),
|
|
4786
|
+
installationId: z32.number(),
|
|
4787
|
+
organizationId: z32.string(),
|
|
4788
|
+
accountLogin: z32.string(),
|
|
4789
|
+
accountId: z32.number(),
|
|
4790
|
+
accountType: z32.string(),
|
|
4616
4791
|
status: GitHubInstallationStatusSchema,
|
|
4617
|
-
createdAt:
|
|
4618
|
-
updatedAt:
|
|
4792
|
+
createdAt: z32.date(),
|
|
4793
|
+
updatedAt: z32.date()
|
|
4619
4794
|
});
|
|
4620
4795
|
}
|
|
4621
4796
|
});
|
|
4622
4797
|
|
|
4623
4798
|
// ../../packages/types/src/schemas/vercel.ts
|
|
4624
|
-
import { z as
|
|
4799
|
+
import { z as z33 } from "zod";
|
|
4625
4800
|
var VercelInstallationWireStatusSchema;
|
|
4626
4801
|
var init_vercel = __esm({
|
|
4627
4802
|
"../../packages/types/src/schemas/vercel.ts"() {
|
|
4628
4803
|
"use strict";
|
|
4629
4804
|
init_esm_shims();
|
|
4630
|
-
VercelInstallationWireStatusSchema =
|
|
4805
|
+
VercelInstallationWireStatusSchema = z33.enum([
|
|
4631
4806
|
"ready",
|
|
4632
4807
|
"pending",
|
|
4633
4808
|
"onboarding",
|
|
@@ -4683,6 +4858,15 @@ var init_preview_url = __esm({
|
|
|
4683
4858
|
"../../packages/types/src/preview-url.ts"() {
|
|
4684
4859
|
"use strict";
|
|
4685
4860
|
init_esm_shims();
|
|
4861
|
+
init_parse_url();
|
|
4862
|
+
}
|
|
4863
|
+
});
|
|
4864
|
+
|
|
4865
|
+
// ../../packages/types/src/org-domain.ts
|
|
4866
|
+
var init_org_domain = __esm({
|
|
4867
|
+
"../../packages/types/src/org-domain.ts"() {
|
|
4868
|
+
"use strict";
|
|
4869
|
+
init_esm_shims();
|
|
4686
4870
|
}
|
|
4687
4871
|
});
|
|
4688
4872
|
|
|
@@ -4734,6 +4918,32 @@ var init_analytics_headers = __esm({
|
|
|
4734
4918
|
}
|
|
4735
4919
|
});
|
|
4736
4920
|
|
|
4921
|
+
// ../../packages/types/src/constants/application-instructions.ts
|
|
4922
|
+
var init_application_instructions = __esm({
|
|
4923
|
+
"../../packages/types/src/constants/application-instructions.ts"() {
|
|
4924
|
+
"use strict";
|
|
4925
|
+
init_esm_shims();
|
|
4926
|
+
}
|
|
4927
|
+
});
|
|
4928
|
+
|
|
4929
|
+
// ../../packages/types/src/constants/auth-cookies.ts
|
|
4930
|
+
var init_auth_cookies = __esm({
|
|
4931
|
+
"../../packages/types/src/constants/auth-cookies.ts"() {
|
|
4932
|
+
"use strict";
|
|
4933
|
+
init_esm_shims();
|
|
4934
|
+
}
|
|
4935
|
+
});
|
|
4936
|
+
|
|
4937
|
+
// ../../packages/types/src/constants/integration-branch.ts
|
|
4938
|
+
var INTEGRATION_BRANCH;
|
|
4939
|
+
var init_integration_branch = __esm({
|
|
4940
|
+
"../../packages/types/src/constants/integration-branch.ts"() {
|
|
4941
|
+
"use strict";
|
|
4942
|
+
init_esm_shims();
|
|
4943
|
+
INTEGRATION_BRANCH = "autonoma-integration";
|
|
4944
|
+
}
|
|
4945
|
+
});
|
|
4946
|
+
|
|
4737
4947
|
// ../../packages/types/src/constants/pipeline-labels.ts
|
|
4738
4948
|
var init_pipeline_labels = __esm({
|
|
4739
4949
|
"../../packages/types/src/constants/pipeline-labels.ts"() {
|
|
@@ -4748,7 +4958,10 @@ var init_constants = __esm({
|
|
|
4748
4958
|
"use strict";
|
|
4749
4959
|
init_esm_shims();
|
|
4750
4960
|
init_analytics_headers();
|
|
4961
|
+
init_application_instructions();
|
|
4962
|
+
init_auth_cookies();
|
|
4751
4963
|
init_billing();
|
|
4964
|
+
init_integration_branch();
|
|
4752
4965
|
init_pipeline_labels();
|
|
4753
4966
|
}
|
|
4754
4967
|
});
|
|
@@ -4761,14 +4974,6 @@ var init_architecture = __esm({
|
|
|
4761
4974
|
}
|
|
4762
4975
|
});
|
|
4763
4976
|
|
|
4764
|
-
// ../../packages/types/src/types/previewkit.ts
|
|
4765
|
-
var init_previewkit = __esm({
|
|
4766
|
-
"../../packages/types/src/types/previewkit.ts"() {
|
|
4767
|
-
"use strict";
|
|
4768
|
-
init_esm_shims();
|
|
4769
|
-
}
|
|
4770
|
-
});
|
|
4771
|
-
|
|
4772
4977
|
// ../../packages/types/src/types/parse-string-record.ts
|
|
4773
4978
|
var init_parse_string_record = __esm({
|
|
4774
4979
|
"../../packages/types/src/types/parse-string-record.ts"() {
|
|
@@ -4805,7 +5010,10 @@ var init_src = __esm({
|
|
|
4805
5010
|
init_github();
|
|
4806
5011
|
init_vercel();
|
|
4807
5012
|
init_sensitive_detection();
|
|
5013
|
+
init_parse_url();
|
|
4808
5014
|
init_preview_url();
|
|
5015
|
+
init_sdk_endpoint();
|
|
5016
|
+
init_org_domain();
|
|
4809
5017
|
init_app_links();
|
|
4810
5018
|
init_agent_handoff_links();
|
|
4811
5019
|
init_sdk_error_signals();
|
|
@@ -5260,7 +5468,7 @@ var init_output = __esm({
|
|
|
5260
5468
|
// src/core/preferences.ts
|
|
5261
5469
|
import { mkdir as mkdir2, readFile as readFile4, writeFile as writeFile3 } from "fs/promises";
|
|
5262
5470
|
import { join as join12 } from "path";
|
|
5263
|
-
import { z as
|
|
5471
|
+
import { z as z35 } from "zod";
|
|
5264
5472
|
async function readPreferences() {
|
|
5265
5473
|
try {
|
|
5266
5474
|
const raw = await readFile4(PREFERENCES_FILE, "utf-8");
|
|
@@ -5295,9 +5503,9 @@ var init_preferences = __esm({
|
|
|
5295
5503
|
init_debug();
|
|
5296
5504
|
init_output();
|
|
5297
5505
|
PREFERENCES_FILE = join12(AUTONOMA_HOME3, "preferences.json");
|
|
5298
|
-
PreferencesSchema =
|
|
5506
|
+
PreferencesSchema = z35.object({
|
|
5299
5507
|
/** The coding agent id (`claude`, `codex`) last chosen from the picker. */
|
|
5300
|
-
agentId:
|
|
5508
|
+
agentId: z35.string().optional()
|
|
5301
5509
|
});
|
|
5302
5510
|
}
|
|
5303
5511
|
});
|
|
@@ -5314,9 +5522,11 @@ __export(coding_agent_exports, {
|
|
|
5314
5522
|
buildAllLaunchers: () => buildAllLaunchers,
|
|
5315
5523
|
isSpawnedByPlanner: () => isSpawnedByPlanner2,
|
|
5316
5524
|
parsePermissionMode: () => parsePermissionMode,
|
|
5317
|
-
selectLauncher: () => selectLauncher
|
|
5318
|
-
selectPermissionMode: () => selectPermissionMode
|
|
5525
|
+
selectLauncher: () => selectLauncher
|
|
5319
5526
|
});
|
|
5527
|
+
import { readFile as readFile5, writeFile as writeFile4 } from "fs/promises";
|
|
5528
|
+
import { homedir as homedir5 } from "os";
|
|
5529
|
+
import { join as join13 } from "path";
|
|
5320
5530
|
import spawn from "cross-spawn";
|
|
5321
5531
|
import which from "which";
|
|
5322
5532
|
function isSpawnedByPlanner2(env = process.env) {
|
|
@@ -5359,28 +5569,32 @@ async function selectLauncher(launchers, presetId, interactive = true) {
|
|
|
5359
5569
|
await updatePreferences({ agentId: selected });
|
|
5360
5570
|
return available.find((l) => l.id === selected);
|
|
5361
5571
|
}
|
|
5362
|
-
async function selectPermissionMode(preset) {
|
|
5363
|
-
if (preset != null) return preset;
|
|
5364
|
-
const selected = await select({
|
|
5365
|
-
message: "How much autonomy should the agent have?",
|
|
5366
|
-
options: [
|
|
5367
|
-
{ value: "bypassPermissions", label: PERMISSION_MODE_LABELS.bypassPermissions },
|
|
5368
|
-
{ value: "acceptEdits", label: PERMISSION_MODE_LABELS.acceptEdits },
|
|
5369
|
-
{ value: "default", label: PERMISSION_MODE_LABELS.default }
|
|
5370
|
-
],
|
|
5371
|
-
initialValue: DEFAULT_PERMISSION_MODE
|
|
5372
|
-
});
|
|
5373
|
-
if (isCancel(selected)) throw new Error("Permission mode selection cancelled");
|
|
5374
|
-
return selected;
|
|
5375
|
-
}
|
|
5376
5572
|
function parsePermissionMode(value) {
|
|
5377
5573
|
if (value === "default" || value === "acceptEdits" || value === "bypassPermissions") return value;
|
|
5378
5574
|
return void 0;
|
|
5379
5575
|
}
|
|
5576
|
+
function describeCommandFailure(result, label) {
|
|
5577
|
+
return explainFailureReason(firstLine(result.stderr) || firstLine(result.stdout) || "unknown error", label);
|
|
5578
|
+
}
|
|
5579
|
+
function isClientNotExecutable(output) {
|
|
5580
|
+
return CLIENT_NOT_EXECUTABLE_PATTERNS.some((pattern) => pattern.test(output));
|
|
5581
|
+
}
|
|
5582
|
+
function explainFailureReason(reason, label) {
|
|
5583
|
+
if (!isClientNotExecutable(reason)) return reason;
|
|
5584
|
+
return `${label} is on your PATH but could not be run (${reason}). Reinstall it, then run again.`;
|
|
5585
|
+
}
|
|
5586
|
+
function describeSignInFailure(login, label) {
|
|
5587
|
+
const reason = firstLine(login.stderr);
|
|
5588
|
+
const code = login.code ?? "unknown";
|
|
5589
|
+
return reason.length > 0 ? ` (exit ${code}: ${explainFailureReason(reason, label)})` : ` (exit ${code})`;
|
|
5590
|
+
}
|
|
5591
|
+
function isRecord2(value) {
|
|
5592
|
+
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
5593
|
+
}
|
|
5380
5594
|
function firstLine(output) {
|
|
5381
5595
|
return output.split("\n").find((line) => line.trim().length > 0)?.trim() ?? "";
|
|
5382
5596
|
}
|
|
5383
|
-
var MCP_COMMAND_TIMEOUT_MS, CODEX_BEARER_TOKEN_ENV, CLAUDE_CONNECTED_MARKER, DEFAULT_PERMISSION_MODE, PERMISSION_MODE_LABELS, CLAUDE_ID, CODEX_ID, BaseLauncher, ClaudeLauncher, CodexLauncher;
|
|
5597
|
+
var MCP_COMMAND_TIMEOUT_MS, ATTACHED_STDERR_CAP, CODEX_BEARER_TOKEN_ENV, CLAUDE_CONNECTED_MARKER, CLIENT_NOT_EXECUTABLE_PATTERNS, CLAUDE_AUTH_HEADER_MARKER, CLAUDE_NEEDS_AUTH_CACHE_FILE, CLAUDE_MODEL, CLAUDE_SUBAGENT_MODEL_ENV, DEFAULT_PERMISSION_MODE, PERMISSION_MODE_LABELS, CLAUDE_ID, CODEX_ID, BaseLauncher, ClaudeLauncher, CodexLauncher;
|
|
5384
5598
|
var init_coding_agent = __esm({
|
|
5385
5599
|
"src/core/coding-agent.ts"() {
|
|
5386
5600
|
"use strict";
|
|
@@ -5388,11 +5602,23 @@ var init_coding_agent = __esm({
|
|
|
5388
5602
|
init_prompts();
|
|
5389
5603
|
init_agent_env();
|
|
5390
5604
|
init_debug();
|
|
5605
|
+
init_logs();
|
|
5391
5606
|
init_preferences();
|
|
5392
5607
|
init_agent_env();
|
|
5393
5608
|
MCP_COMMAND_TIMEOUT_MS = 6e4;
|
|
5609
|
+
ATTACHED_STDERR_CAP = 4e3;
|
|
5394
5610
|
CODEX_BEARER_TOKEN_ENV = "AUTONOMA_MCP_TOKEN";
|
|
5395
5611
|
CLAUDE_CONNECTED_MARKER = "Connected";
|
|
5612
|
+
CLIENT_NOT_EXECUTABLE_PATTERNS = [
|
|
5613
|
+
/is not recognized as an internal or external command/i,
|
|
5614
|
+
/command not found/i,
|
|
5615
|
+
/\bENOENT\b/,
|
|
5616
|
+
/no such file or directory/i
|
|
5617
|
+
];
|
|
5618
|
+
CLAUDE_AUTH_HEADER_MARKER = "Authorization:";
|
|
5619
|
+
CLAUDE_NEEDS_AUTH_CACHE_FILE = "mcp-needs-auth-cache.json";
|
|
5620
|
+
CLAUDE_MODEL = "opus";
|
|
5621
|
+
CLAUDE_SUBAGENT_MODEL_ENV = "CLAUDE_CODE_SUBAGENT_MODEL";
|
|
5396
5622
|
DEFAULT_PERMISSION_MODE = "bypassPermissions";
|
|
5397
5623
|
PERMISSION_MODE_LABELS = {
|
|
5398
5624
|
default: "Approve each command",
|
|
@@ -5479,40 +5705,107 @@ var init_coding_agent = __esm({
|
|
|
5479
5705
|
/**
|
|
5480
5706
|
* Sign this client in to a server through its own browser flow.
|
|
5481
5707
|
*
|
|
5482
|
-
*
|
|
5483
|
-
*
|
|
5484
|
-
*
|
|
5485
|
-
*
|
|
5486
|
-
*
|
|
5708
|
+
* Never throws. A sign-in is a convenience over the credential the run is already
|
|
5709
|
+
* holding, so every way it can fail - no terminal to open a browser from, a
|
|
5710
|
+
* client that exits non-zero - resolves to a result the caller can fall back on.
|
|
5711
|
+
* Refusing without a terminal is still the right call: the flow prints a URL and
|
|
5712
|
+
* blocks on a callback, so with nobody at the keyboard it would not fail, it would
|
|
5713
|
+
* hang indefinitely on a prompt nobody can see.
|
|
5487
5714
|
*/
|
|
5488
5715
|
async signIn(serverName) {
|
|
5489
5716
|
if (process.stdin.isTTY !== true) {
|
|
5490
|
-
|
|
5491
|
-
`Cannot sign ${this.label} in to the ${serverName} MCP server without a terminal: that flow opens a browser and waits for it. Set AUTONOMA_API_TOKEN so the run authorizes with an API key instead.`
|
|
5492
|
-
);
|
|
5717
|
+
return { ok: false, detail: " (no terminal to run a browser sign-in from)", clientUnusable: false };
|
|
5493
5718
|
}
|
|
5494
5719
|
log.info(`Authorizing ${this.label} with Autonoma - approve it in the browser that opens.`);
|
|
5495
5720
|
const login = await this.runAttached(["mcp", "login", serverName]);
|
|
5496
|
-
if (login
|
|
5721
|
+
if (login.code === 0) return { ok: true, detail: "", clientUnusable: false };
|
|
5722
|
+
debugLog(`${this.label} sign-in failed`, { serverName, code: login.code, stderr: login.stderr });
|
|
5723
|
+
return {
|
|
5724
|
+
ok: false,
|
|
5725
|
+
detail: describeSignInFailure(login, this.label),
|
|
5726
|
+
clientUnusable: isClientNotExecutable(login.stderr)
|
|
5727
|
+
};
|
|
5728
|
+
}
|
|
5729
|
+
/**
|
|
5730
|
+
* The credential a registration must have when no browser sign-in is on the table.
|
|
5731
|
+
* Its absence is a configuration error worth stating now, rather than an
|
|
5732
|
+
* unauthorized server the spawned agent discovers on its first tool call.
|
|
5733
|
+
*/
|
|
5734
|
+
requireHeadlessToken(spec) {
|
|
5735
|
+
if (spec.apiToken != null) return spec.apiToken;
|
|
5736
|
+
throw new Error(
|
|
5737
|
+
`Cannot authorize the ${spec.name} MCP server for ${this.label} without a terminal: a browser sign-in needs someone to approve it. Set AUTONOMA_API_TOKEN so the run authorizes with an API key instead.`
|
|
5738
|
+
);
|
|
5739
|
+
}
|
|
5740
|
+
/**
|
|
5741
|
+
* Authorize a server that a browser sign-in was preferred for, and say how it
|
|
5742
|
+
* ended up authorized so the caller can register it accordingly.
|
|
5743
|
+
*
|
|
5744
|
+
* A failed sign-in is not the end of the run. The token that falls back here is
|
|
5745
|
+
* the same one that already authorized every API call made to get this far, so
|
|
5746
|
+
* refusing to use it loses the run to a browser that would not open. It also
|
|
5747
|
+
* repairs what the attempt broke: a failed `mcp login` clears whatever stored
|
|
5748
|
+
* credentials the client had, which is why a first failure used to make every
|
|
5749
|
+
* later run fail too.
|
|
5750
|
+
*/
|
|
5751
|
+
async authorizeInteractively(spec) {
|
|
5752
|
+
const signIn = await this.signIn(spec.name);
|
|
5753
|
+
if (signIn.ok) return { via: "sign-in" };
|
|
5754
|
+
return { via: "token", ...this.fallBackToToken(spec, signIn) };
|
|
5755
|
+
}
|
|
5756
|
+
/**
|
|
5757
|
+
* Switch to the run's own API token after a browser sign-in did not happen, for
|
|
5758
|
+
* whatever reason - it was refused, it failed, or the client folded it into a
|
|
5759
|
+
* registration that failed. Throws only when there is no token to switch to.
|
|
5760
|
+
*/
|
|
5761
|
+
fallBackToToken(spec, signIn) {
|
|
5762
|
+
const detail = signIn.detail;
|
|
5763
|
+
if (signIn.clientUnusable) {
|
|
5764
|
+
throw new Error(`Could not register the ${spec.name} MCP server with ${this.label}:${detail}`);
|
|
5765
|
+
}
|
|
5766
|
+
if (spec.apiToken == null) {
|
|
5497
5767
|
throw new Error(
|
|
5498
|
-
`${this.label} could not sign in to the ${
|
|
5768
|
+
`${this.label} could not sign in to the ${spec.name} MCP server${detail}, and this run has no API token to fall back on. Set AUTONOMA_API_TOKEN and run again.`
|
|
5499
5769
|
);
|
|
5500
5770
|
}
|
|
5771
|
+
captureLog("warn", "Browser sign-in failed; authorizing the MCP server with the run's API token", {
|
|
5772
|
+
source: "coding_agent",
|
|
5773
|
+
agent: this.id,
|
|
5774
|
+
detail
|
|
5775
|
+
});
|
|
5776
|
+
log.warn(
|
|
5777
|
+
`${this.label} couldn't sign in to the ${spec.name} MCP server${detail}. Authorizing it with this run's API token instead.`
|
|
5778
|
+
);
|
|
5779
|
+
return { apiToken: spec.apiToken };
|
|
5501
5780
|
}
|
|
5502
5781
|
/**
|
|
5503
5782
|
* Run a subcommand that hands the terminal over - an interactive OAuth sign-in.
|
|
5504
|
-
* The client prints a URL, opens a browser and waits on the callback, so
|
|
5505
|
-
*
|
|
5783
|
+
* The client prints a URL, opens a browser and waits on the callback, so stdin and
|
|
5784
|
+
* stdout stay attached to this process.
|
|
5785
|
+
*
|
|
5786
|
+
* stderr is the exception: it is piped so the client's own diagnosis can be quoted
|
|
5787
|
+
* back and reported, and echoed as it arrives so the user still watches it live.
|
|
5788
|
+
* Inheriting it would put that text on screen and nowhere else, which is what made
|
|
5789
|
+
* every sign-in failure look identical from the outside.
|
|
5506
5790
|
*/
|
|
5507
5791
|
runAttached(args) {
|
|
5508
5792
|
debugLog(`Running ${this.command} ${args.join(" ")} attached`);
|
|
5509
5793
|
return new Promise((resolve6) => {
|
|
5510
|
-
const proc = spawn(this.command, args, {
|
|
5794
|
+
const proc = spawn(this.command, args, {
|
|
5795
|
+
cwd: this.opts.cwd,
|
|
5796
|
+
env: this.opts.env,
|
|
5797
|
+
stdio: ["inherit", "inherit", "pipe"]
|
|
5798
|
+
});
|
|
5799
|
+
let stderr = "";
|
|
5800
|
+
proc.stderr?.on("data", (chunk) => {
|
|
5801
|
+
process.stderr.write(chunk);
|
|
5802
|
+
if (stderr.length < ATTACHED_STDERR_CAP) stderr += chunk.toString();
|
|
5803
|
+
});
|
|
5511
5804
|
proc.on("error", (err) => {
|
|
5512
5805
|
debugLog(`${this.command} ${args[0] ?? ""} failed to spawn`, { err });
|
|
5513
|
-
resolve6(void 0);
|
|
5806
|
+
resolve6({ code: void 0, stderr: err.message });
|
|
5514
5807
|
});
|
|
5515
|
-
proc.on("close", (code) => resolve6(code ?? void 0));
|
|
5808
|
+
proc.on("close", (code) => resolve6({ code: code ?? void 0, stderr }));
|
|
5516
5809
|
});
|
|
5517
5810
|
}
|
|
5518
5811
|
};
|
|
@@ -5521,36 +5814,113 @@ var init_coding_agent = __esm({
|
|
|
5521
5814
|
label = "Claude Code";
|
|
5522
5815
|
command = CLAUDE_ID;
|
|
5523
5816
|
buildArgs(message, permissionMode, interactive) {
|
|
5524
|
-
return interactive ? ["--permission-mode", permissionMode, message] : ["-p", message, "--permission-mode", permissionMode, "--verbose"];
|
|
5817
|
+
return interactive ? ["--permission-mode", permissionMode, "--model", CLAUDE_MODEL, message] : ["-p", message, "--permission-mode", permissionMode, "--model", CLAUDE_MODEL, "--verbose"];
|
|
5818
|
+
}
|
|
5819
|
+
/** Carry the model floor into the session's subagents, which read it from the env. */
|
|
5820
|
+
spawnEnv(extra) {
|
|
5821
|
+
return { ...super.spawnEnv(extra), [CLAUDE_SUBAGENT_MODEL_ENV]: CLAUDE_MODEL };
|
|
5525
5822
|
}
|
|
5526
5823
|
/**
|
|
5527
|
-
*
|
|
5528
|
-
*
|
|
5529
|
-
*
|
|
5530
|
-
* was never authorized - the agent simply has no Autonoma tools.
|
|
5531
|
-
*
|
|
5532
|
-
* With a token the header goes into Claude's own config and there is nothing to
|
|
5533
|
-
* sign in to. Without one, the browser sign-in runs attached to the terminal.
|
|
5824
|
+
* Headless registers with the bearer header outright - there is no browser to sign
|
|
5825
|
+
* in with. Interactively the registration goes in without one so the client's own
|
|
5826
|
+
* sign-in can claim it, and the header is applied only if that sign-in fails.
|
|
5534
5827
|
*/
|
|
5535
5828
|
async registerMcpServer(spec) {
|
|
5536
|
-
const
|
|
5537
|
-
|
|
5538
|
-
|
|
5539
|
-
|
|
5540
|
-
|
|
5541
|
-
|
|
5829
|
+
const registration = await this.register(spec);
|
|
5830
|
+
await this.forgetNeedsAuthVerdict(spec.name);
|
|
5831
|
+
return registration;
|
|
5832
|
+
}
|
|
5833
|
+
/**
|
|
5834
|
+
* Forget Claude's cached "this server needs authorization" verdict for this name.
|
|
5835
|
+
*
|
|
5836
|
+
* Claude caches that verdict per server NAME and then skips connecting on every
|
|
5837
|
+
* later session - "Skipping connection (cached needs-auth)" in its own debug log -
|
|
5838
|
+
* whatever the registration now holds. The health check this class runs to decide
|
|
5839
|
+
* whether to sign in is itself what writes the entry, and a successful one does not
|
|
5840
|
+
* clear it. Without this, a registration can report Connected, answer tool calls
|
|
5841
|
+
* over HTTP, and still be invisible to the agent we spawn: no Autonoma tools, and
|
|
5842
|
+
* nothing on screen saying why.
|
|
5843
|
+
*
|
|
5844
|
+
* Best effort by design. It reaches into another tool's internal state, so a missing
|
|
5845
|
+
* file, a changed format or an unwritable home degrades to a debug line rather than
|
|
5846
|
+
* failing a registration that is otherwise fine.
|
|
5847
|
+
*/
|
|
5848
|
+
async forgetNeedsAuthVerdict(serverName) {
|
|
5849
|
+
const configDir = this.opts.env.CLAUDE_CONFIG_DIR ?? join13(homedir5(), ".claude");
|
|
5850
|
+
const cachePath = join13(configDir, CLAUDE_NEEDS_AUTH_CACHE_FILE);
|
|
5851
|
+
try {
|
|
5852
|
+
const parsed = JSON.parse(await readFile5(cachePath, "utf-8"));
|
|
5853
|
+
if (!isRecord2(parsed) || !(serverName in parsed)) return;
|
|
5854
|
+
const remaining = { ...parsed };
|
|
5855
|
+
delete remaining[serverName];
|
|
5856
|
+
await writeFile4(cachePath, JSON.stringify(remaining), "utf-8");
|
|
5857
|
+
debugLog("Cleared a cached needs-auth verdict", { server: serverName });
|
|
5858
|
+
} catch (err) {
|
|
5859
|
+
debugLog("Could not clear the cached needs-auth verdict", { cachePath, err });
|
|
5860
|
+
}
|
|
5861
|
+
}
|
|
5862
|
+
async register(spec) {
|
|
5863
|
+
await this.dropRegistrationPointingElsewhere(spec);
|
|
5864
|
+
if (!spec.browserSignIn) {
|
|
5865
|
+
await this.addServer(spec, this.requireHeadlessToken(spec));
|
|
5866
|
+
return { env: {} };
|
|
5542
5867
|
}
|
|
5868
|
+
const registered = await this.addServer(spec, void 0);
|
|
5869
|
+
if (registered.includes(CLAUDE_CONNECTED_MARKER)) return { env: {} };
|
|
5870
|
+
const authorizable = registered.includes(CLAUDE_AUTH_HEADER_MARKER) ? await this.clearBearerHeader(spec) : registered;
|
|
5871
|
+
if (authorizable.includes(CLAUDE_CONNECTED_MARKER)) return { env: {} };
|
|
5872
|
+
const authorization = await this.authorizeInteractively(spec);
|
|
5873
|
+
if (authorization.via === "sign-in") return { env: {} };
|
|
5874
|
+
await this.runCommand(["mcp", "remove", spec.name, "-s", "user"]);
|
|
5875
|
+
await this.addServer(spec, authorization.apiToken);
|
|
5876
|
+
return { env: {} };
|
|
5877
|
+
}
|
|
5878
|
+
/**
|
|
5879
|
+
* An existing registration is only useful if it points where this run does. `mcp
|
|
5880
|
+
* add` will not update one, so a registration left over from another environment
|
|
5881
|
+
* would be reused in silence and the agent would work against the wrong Autonoma.
|
|
5882
|
+
* Replace it rather than inherit it.
|
|
5883
|
+
*/
|
|
5884
|
+
async dropRegistrationPointingElsewhere(spec) {
|
|
5885
|
+
const existing = await this.runCommand(["mcp", "get", spec.name]);
|
|
5886
|
+
if (existing.code !== 0 || existing.stdout.includes(spec.url)) return;
|
|
5887
|
+
debugLog("Replacing an MCP registration that points elsewhere", { server: spec.name, url: spec.url });
|
|
5888
|
+
await this.runCommand(["mcp", "remove", spec.name, "-s", "user"]);
|
|
5889
|
+
}
|
|
5890
|
+
/**
|
|
5891
|
+
* Re-register without the bearer header, so a browser sign-in becomes possible
|
|
5892
|
+
* again. Returns what `get` then reported - which may be Connected on its own, if
|
|
5893
|
+
* the client still holds OAuth credentials the header had been masking.
|
|
5894
|
+
*/
|
|
5895
|
+
async clearBearerHeader(spec) {
|
|
5896
|
+
debugLog("Clearing a bearer header that would block the browser sign-in", { server: spec.name });
|
|
5897
|
+
await this.runCommand(["mcp", "remove", spec.name, "-s", "user"]);
|
|
5898
|
+
return await this.addServer(spec, void 0);
|
|
5899
|
+
}
|
|
5900
|
+
/**
|
|
5901
|
+
* Add the server and confirm the client actually holds it, returning what `get`
|
|
5902
|
+
* reported so the caller can also read whether it is authorized.
|
|
5903
|
+
*
|
|
5904
|
+
* `mcp add` exits non-zero when the server is already registered, which is the
|
|
5905
|
+
* normal case on every run after the first, so its exit code is a hint and `get`
|
|
5906
|
+
* is the verdict.
|
|
5907
|
+
*
|
|
5908
|
+
* `--scope user` and not the default (`local`): the default binds the server to
|
|
5909
|
+
* the directory the command ran in. The planner can be invoked from anywhere in a
|
|
5910
|
+
* repo, and a server registered against the wrong directory looks exactly like one
|
|
5911
|
+
* that was never authorized - the agent simply has no Autonoma tools.
|
|
5912
|
+
*/
|
|
5913
|
+
async addServer(spec, apiToken) {
|
|
5914
|
+
const add = ["mcp", "add", "--transport", "http", "--scope", "user", spec.name, spec.url];
|
|
5915
|
+
if (apiToken != null) add.push("--header", `Authorization: Bearer ${apiToken}`);
|
|
5543
5916
|
await this.runCommand(add);
|
|
5544
5917
|
const registered = await this.runCommand(["mcp", "get", spec.name]);
|
|
5545
5918
|
if (registered.code !== 0) {
|
|
5546
5919
|
throw new Error(
|
|
5547
|
-
`Could not register the ${spec.name} MCP server with ${this.label}: ${
|
|
5920
|
+
`Could not register the ${spec.name} MCP server with ${this.label}: ${describeCommandFailure(registered, this.label)}`
|
|
5548
5921
|
);
|
|
5549
5922
|
}
|
|
5550
|
-
|
|
5551
|
-
await this.signIn(spec.name);
|
|
5552
|
-
}
|
|
5553
|
-
return { env: {} };
|
|
5923
|
+
return registered.stdout;
|
|
5554
5924
|
}
|
|
5555
5925
|
};
|
|
5556
5926
|
CodexLauncher = class extends BaseLauncher {
|
|
@@ -5587,19 +5957,53 @@ var init_coding_agent = __esm({
|
|
|
5587
5957
|
* server overwrites its entry, so a re-run cannot leave a stale URL behind.
|
|
5588
5958
|
*/
|
|
5589
5959
|
async registerMcpServer(spec) {
|
|
5960
|
+
if (!spec.browserSignIn) {
|
|
5961
|
+
const apiToken = this.requireHeadlessToken(spec);
|
|
5962
|
+
await this.addServer(spec, apiToken);
|
|
5963
|
+
return { env: { [CODEX_BEARER_TOKEN_ENV]: apiToken } };
|
|
5964
|
+
}
|
|
5965
|
+
const signIn = await this.addServerAndSignIn(spec);
|
|
5966
|
+
if (signIn.ok) return { env: {} };
|
|
5967
|
+
const authorization = this.fallBackToToken(spec, signIn);
|
|
5968
|
+
await this.addServer(spec, authorization.apiToken);
|
|
5969
|
+
return { env: { [CODEX_BEARER_TOKEN_ENV]: authorization.apiToken } };
|
|
5970
|
+
}
|
|
5971
|
+
/**
|
|
5972
|
+
* Register the server the interactive way, which for Codex is one step rather than
|
|
5973
|
+
* two: given a `--url` and no bearer token, `codex mcp add` detects the server's
|
|
5974
|
+
* OAuth support, prints an authorization URL, opens a browser and waits on the
|
|
5975
|
+
* callback - all inside the add.
|
|
5976
|
+
*
|
|
5977
|
+
* Two consequences. It needs the terminal: run through a pipe, the URL it tells the
|
|
5978
|
+
* user to "copy above manually" when the browser fails to open goes into a buffer
|
|
5979
|
+
* nobody reads, turning a recoverable prompt into a dead end. And a clean exit means
|
|
5980
|
+
* the server is registered AND signed in, so following it with `mcp login` would
|
|
5981
|
+
* only open a second browser flow for a session that is already authorized.
|
|
5982
|
+
*/
|
|
5983
|
+
async addServerAndSignIn(spec) {
|
|
5984
|
+
if (process.stdin.isTTY !== true) {
|
|
5985
|
+
return { ok: false, detail: " (no terminal to run a browser sign-in from)", clientUnusable: false };
|
|
5986
|
+
}
|
|
5987
|
+
log.info(`Authorizing ${this.label} with Autonoma - approve it in the browser that opens.`);
|
|
5988
|
+
const added = await this.runAttached(["mcp", "add", spec.name, "--url", spec.url]);
|
|
5989
|
+
if (added.code === 0) return { ok: true, detail: "", clientUnusable: false };
|
|
5990
|
+
debugLog(`${this.label} registration sign-in failed`, { code: added.code, stderr: added.stderr });
|
|
5991
|
+
return {
|
|
5992
|
+
ok: false,
|
|
5993
|
+
detail: describeSignInFailure(added, this.label),
|
|
5994
|
+
clientUnusable: isClientNotExecutable(added.stderr)
|
|
5995
|
+
};
|
|
5996
|
+
}
|
|
5997
|
+
/** Add (or overwrite) the server, with the token read from an env var by name. */
|
|
5998
|
+
async addServer(spec, apiToken) {
|
|
5590
5999
|
const add = ["mcp", "add", spec.name, "--url", spec.url];
|
|
5591
|
-
if (
|
|
6000
|
+
if (apiToken != null) add.push("--bearer-token-env-var", CODEX_BEARER_TOKEN_ENV);
|
|
5592
6001
|
const added = await this.runCommand(add);
|
|
5593
6002
|
if (added.code !== 0) {
|
|
5594
6003
|
throw new Error(
|
|
5595
|
-
`Could not register the ${spec.name} MCP server with ${this.label}: ${
|
|
6004
|
+
`Could not register the ${spec.name} MCP server with ${this.label}: ${describeCommandFailure(added, this.label)}`
|
|
5596
6005
|
);
|
|
5597
6006
|
}
|
|
5598
|
-
if (spec.apiToken == null) {
|
|
5599
|
-
await this.signIn(spec.name);
|
|
5600
|
-
return { env: {} };
|
|
5601
|
-
}
|
|
5602
|
-
return { env: { [CODEX_BEARER_TOKEN_ENV]: spec.apiToken } };
|
|
5603
6007
|
}
|
|
5604
6008
|
};
|
|
5605
6009
|
}
|
|
@@ -6242,10 +6646,41 @@ var init_interrupt = __esm({
|
|
|
6242
6646
|
}
|
|
6243
6647
|
});
|
|
6244
6648
|
|
|
6649
|
+
// src/core/permission-mode.ts
|
|
6650
|
+
async function resolvePermissionMode(request) {
|
|
6651
|
+
const known = request.preset ?? runPermissionMode ?? request.remembered;
|
|
6652
|
+
if (known != null) {
|
|
6653
|
+
runPermissionMode = known;
|
|
6654
|
+
return known;
|
|
6655
|
+
}
|
|
6656
|
+
if (!request.interactive) return DEFAULT_PERMISSION_MODE;
|
|
6657
|
+
const selected = await select({
|
|
6658
|
+
message: "How much autonomy should the agent have?",
|
|
6659
|
+
options: [
|
|
6660
|
+
{ value: "bypassPermissions", label: PERMISSION_MODE_LABELS.bypassPermissions },
|
|
6661
|
+
{ value: "acceptEdits", label: PERMISSION_MODE_LABELS.acceptEdits },
|
|
6662
|
+
{ value: "default", label: PERMISSION_MODE_LABELS.default }
|
|
6663
|
+
],
|
|
6664
|
+
initialValue: DEFAULT_PERMISSION_MODE
|
|
6665
|
+
});
|
|
6666
|
+
if (isCancel(selected)) throw new Error("Permission mode selection cancelled");
|
|
6667
|
+
runPermissionMode = selected;
|
|
6668
|
+
return selected;
|
|
6669
|
+
}
|
|
6670
|
+
var runPermissionMode;
|
|
6671
|
+
var init_permission_mode = __esm({
|
|
6672
|
+
"src/core/permission-mode.ts"() {
|
|
6673
|
+
"use strict";
|
|
6674
|
+
init_esm_shims();
|
|
6675
|
+
init_prompts();
|
|
6676
|
+
init_coding_agent();
|
|
6677
|
+
}
|
|
6678
|
+
});
|
|
6679
|
+
|
|
6245
6680
|
// src/core/preview-phase.ts
|
|
6246
6681
|
function previewPrompt(code, interactive) {
|
|
6247
6682
|
const instruction = `set up my preview environments with the ${MCP_SERVER_NAME} MCP, code ${code}`;
|
|
6248
|
-
return interactive ? instruction : `${instruction}.${HEADLESS_GUIDANCE}`;
|
|
6683
|
+
return interactive ? `${instruction}.${BRANCH_GUIDANCE}` : `${instruction}.${BRANCH_GUIDANCE}${HEADLESS_GUIDANCE}`;
|
|
6249
6684
|
}
|
|
6250
6685
|
async function runPreviewPhase(deps) {
|
|
6251
6686
|
const timing = deps.timing ?? DEFAULT_PHASE_TIMING;
|
|
@@ -6253,7 +6688,8 @@ async function runPreviewPhase(deps) {
|
|
|
6253
6688
|
const registration = await deps.launcher.registerMcpServer({
|
|
6254
6689
|
name: MCP_SERVER_NAME,
|
|
6255
6690
|
url: deps.mcpUrl,
|
|
6256
|
-
apiToken: deps.apiToken
|
|
6691
|
+
apiToken: deps.apiToken,
|
|
6692
|
+
browserSignIn: deps.interactive
|
|
6257
6693
|
});
|
|
6258
6694
|
const code = await deps.client.createAgentPairing(deps.applicationId);
|
|
6259
6695
|
captureLog("info", "Handing the preview environment to a coding agent", {
|
|
@@ -6313,11 +6749,12 @@ async function readVerifiedState(client, applicationId) {
|
|
|
6313
6749
|
});
|
|
6314
6750
|
return client.getOnboardingState(applicationId);
|
|
6315
6751
|
}
|
|
6316
|
-
var MCP_SERVER_NAME, PHASE_POLL_MS, PHASE_EXIT_GRACE_MS, KILL_ESCALATION_MS, DEFAULT_PHASE_TIMING, HEADLESS_GUIDANCE;
|
|
6752
|
+
var MCP_SERVER_NAME, PHASE_POLL_MS, PHASE_EXIT_GRACE_MS, KILL_ESCALATION_MS, DEFAULT_PHASE_TIMING, HEADLESS_GUIDANCE, BRANCH_GUIDANCE;
|
|
6317
6753
|
var init_preview_phase = __esm({
|
|
6318
6754
|
"src/core/preview-phase.ts"() {
|
|
6319
6755
|
"use strict";
|
|
6320
6756
|
init_esm_shims();
|
|
6757
|
+
init_src();
|
|
6321
6758
|
init_prompts();
|
|
6322
6759
|
init_debug();
|
|
6323
6760
|
init_logs();
|
|
@@ -6332,6 +6769,7 @@ var init_preview_phase = __esm({
|
|
|
6332
6769
|
killMs: KILL_ESCALATION_MS
|
|
6333
6770
|
};
|
|
6334
6771
|
HEADLESS_GUIDANCE = " You are running with no human to answer you, so where you would ask a question, choose the safer option and say which you chose. You are not finished when you have tried - you are finished when the preview is verified. If a deploy fails, read its logs, fix the cause and deploy again. Never report success you have not confirmed.";
|
|
6772
|
+
BRANCH_GUIDANCE = ` Before you change any file: if you are not already on the \`${INTEGRATION_BRANCH}\` branch, create it off the repo's default branch and switch to it now - read the default branch from the remote (\`git symbolic-ref refs/remotes/origin/HEAD\`) rather than assuming it is called main, and never commit onto it. If the working tree already has uncommitted changes they are the developer's: branch off the current HEAD instead and keep them out of your commits. Push the branch before you name it to Autonoma.`;
|
|
6335
6773
|
}
|
|
6336
6774
|
});
|
|
6337
6775
|
|
|
@@ -6351,7 +6789,8 @@ async function runSdkRepairPhase(deps) {
|
|
|
6351
6789
|
const registration = await deps.launcher.registerMcpServer({
|
|
6352
6790
|
name: MCP_SERVER_NAME,
|
|
6353
6791
|
url: deps.mcpUrl,
|
|
6354
|
-
apiToken: deps.apiToken
|
|
6792
|
+
apiToken: deps.apiToken,
|
|
6793
|
+
browserSignIn: deps.interactive
|
|
6355
6794
|
});
|
|
6356
6795
|
const code = await deps.client.createAgentPairing(deps.applicationId);
|
|
6357
6796
|
captureLog("info", "Handing the SDK and dry run to a coding agent", {
|
|
@@ -6461,8 +6900,10 @@ async function runPreviewHandoff(deps) {
|
|
|
6461
6900
|
captureLog("warn", "No coding agent available for the preview phase", { source: "front_door" });
|
|
6462
6901
|
return { kind: "no-agent" };
|
|
6463
6902
|
}
|
|
6464
|
-
const
|
|
6465
|
-
|
|
6903
|
+
const permissionMode = await resolvePermissionMode({
|
|
6904
|
+
preset: parsePermissionMode(config.permissionMode),
|
|
6905
|
+
interactive
|
|
6906
|
+
});
|
|
6466
6907
|
suspend();
|
|
6467
6908
|
let outcome;
|
|
6468
6909
|
try {
|
|
@@ -6471,7 +6912,7 @@ async function runPreviewHandoff(deps) {
|
|
|
6471
6912
|
applicationId: plan.applicationId,
|
|
6472
6913
|
launcher,
|
|
6473
6914
|
permissionMode,
|
|
6474
|
-
apiToken:
|
|
6915
|
+
apiToken: config.autonomaApiToken,
|
|
6475
6916
|
mcpUrl: resolveMcpUrl(config.autonomaApiUrl),
|
|
6476
6917
|
interactive
|
|
6477
6918
|
});
|
|
@@ -6504,8 +6945,10 @@ async function runSdkRepairHandoff(deps) {
|
|
|
6504
6945
|
captureLog("warn", "No coding agent available for the SDK repair phase", { source: "front_door" });
|
|
6505
6946
|
return void 0;
|
|
6506
6947
|
}
|
|
6507
|
-
const
|
|
6508
|
-
|
|
6948
|
+
const permissionMode = await resolvePermissionMode({
|
|
6949
|
+
preset: parsePermissionMode(config.permissionMode),
|
|
6950
|
+
interactive
|
|
6951
|
+
});
|
|
6509
6952
|
suspend();
|
|
6510
6953
|
try {
|
|
6511
6954
|
return await runSdkRepairPhase({
|
|
@@ -6513,7 +6956,7 @@ async function runSdkRepairHandoff(deps) {
|
|
|
6513
6956
|
applicationId: plan.applicationId,
|
|
6514
6957
|
launcher,
|
|
6515
6958
|
permissionMode,
|
|
6516
|
-
apiToken:
|
|
6959
|
+
apiToken: config.autonomaApiToken,
|
|
6517
6960
|
mcpUrl: resolveMcpUrl(config.autonomaApiUrl),
|
|
6518
6961
|
interactive
|
|
6519
6962
|
});
|
|
@@ -6540,6 +6983,7 @@ var init_front_door = __esm({
|
|
|
6540
6983
|
init_interrupt();
|
|
6541
6984
|
init_logs();
|
|
6542
6985
|
init_onboarding_phase();
|
|
6986
|
+
init_permission_mode();
|
|
6543
6987
|
init_preview_phase();
|
|
6544
6988
|
init_sdk_repair_phase();
|
|
6545
6989
|
}
|
|
@@ -6598,16 +7042,16 @@ var init_notify = __esm({
|
|
|
6598
7042
|
});
|
|
6599
7043
|
|
|
6600
7044
|
// src/core/project-map.ts
|
|
6601
|
-
import { readFile as
|
|
6602
|
-
import { join as
|
|
6603
|
-
import { z as
|
|
7045
|
+
import { readFile as readFile7, writeFile as writeFile6 } from "fs/promises";
|
|
7046
|
+
import { join as join15 } from "path";
|
|
7047
|
+
import { z as z36 } from "zod";
|
|
6604
7048
|
async function saveProjectMap(outputDir, map) {
|
|
6605
|
-
await
|
|
7049
|
+
await writeFile6(join15(outputDir, PROJECT_MAP_FILE), JSON.stringify(map, null, 2), "utf-8");
|
|
6606
7050
|
}
|
|
6607
7051
|
async function loadProjectMap(outputDir) {
|
|
6608
|
-
const path3 =
|
|
7052
|
+
const path3 = join15(outputDir, PROJECT_MAP_FILE);
|
|
6609
7053
|
try {
|
|
6610
|
-
const raw = await
|
|
7054
|
+
const raw = await readFile7(path3, "utf-8");
|
|
6611
7055
|
const parsed = ProjectMapSchema.safeParse(JSON.parse(raw));
|
|
6612
7056
|
if (parsed.success) return parsed.data;
|
|
6613
7057
|
debugLog("project-map.json failed schema validation, ignoring it", { path: path3, issues: parsed.error.issues });
|
|
@@ -6709,34 +7153,34 @@ var init_project_map = __esm({
|
|
|
6709
7153
|
"use strict";
|
|
6710
7154
|
init_esm_shims();
|
|
6711
7155
|
init_debug();
|
|
6712
|
-
FrontendEntry =
|
|
6713
|
-
path:
|
|
6714
|
-
framework:
|
|
6715
|
-
dependsOn:
|
|
7156
|
+
FrontendEntry = z36.object({
|
|
7157
|
+
path: z36.string().min(1).describe("Repo-relative path to the frontend app/directory (the UI surface)."),
|
|
7158
|
+
framework: z36.string().describe("Detected UI framework/stack, or 'unknown' if not obvious (e.g. next, react, vue, svelte)."),
|
|
7159
|
+
dependsOn: z36.array(z36.string()).describe(
|
|
6716
7160
|
"Repo-relative paths (each matching a backend in this map) that THIS frontend needs in order to function - the API/service(s) it calls and the data layer(s) that own the records it renders. These are pre-selected when the user picks this frontend, so only list backends this frontend actually depends on. Empty if it needs none."
|
|
6717
7161
|
),
|
|
6718
|
-
why:
|
|
7162
|
+
why: z36.string().min(1).describe("One line: the evidence that made you classify this as a frontend.")
|
|
6719
7163
|
});
|
|
6720
|
-
BackendEntry =
|
|
6721
|
-
path:
|
|
6722
|
-
language:
|
|
6723
|
-
framework:
|
|
6724
|
-
dataLayer:
|
|
6725
|
-
kind:
|
|
6726
|
-
schemaPath:
|
|
7164
|
+
BackendEntry = z36.object({
|
|
7165
|
+
path: z36.string().min(1).describe("Repo-relative path to the backend/API/service or the package that owns the data layer."),
|
|
7166
|
+
language: z36.string().describe("Primary language, or 'unknown' (e.g. typescript, python, go, rust)."),
|
|
7167
|
+
framework: z36.string().describe("Web/service framework or ORM stack, or 'unknown' (e.g. express, hono, fastapi, rails)."),
|
|
7168
|
+
dataLayer: z36.object({
|
|
7169
|
+
kind: z36.string().describe("How models are defined (e.g. prisma, drizzle, sqlalchemy, typeorm, raw-sql, unknown)."),
|
|
7170
|
+
schemaPath: z36.string().optional().describe("Repo-relative path to the schema/models definition, if found.")
|
|
6727
7171
|
}).optional().describe("Where this backend's database models live. Omit if this backend owns no data layer."),
|
|
6728
|
-
why:
|
|
7172
|
+
why: z36.string().min(1).describe("One line: the evidence that made you classify this as a backend.")
|
|
6729
7173
|
});
|
|
6730
|
-
IgnoreEntry =
|
|
6731
|
-
path:
|
|
6732
|
-
why:
|
|
7174
|
+
IgnoreEntry = z36.object({
|
|
7175
|
+
path: z36.string().min(1).describe("Repo-relative path to a directory that is NOT relevant to testing this app."),
|
|
7176
|
+
why: z36.string().min(1).describe("One line: why it is irrelevant (e.g. docs site, infra, tooling, examples, unrelated app).")
|
|
6733
7177
|
});
|
|
6734
|
-
ProjectMapSchema =
|
|
6735
|
-
frontends:
|
|
6736
|
-
backends:
|
|
7178
|
+
ProjectMapSchema = z36.object({
|
|
7179
|
+
frontends: z36.array(FrontendEntry).describe("The UI surface(s) whose pages/flows get tested. Usually 1; may be more."),
|
|
7180
|
+
backends: z36.array(BackendEntry).describe(
|
|
6737
7181
|
"The API/service(s) and data layer(s) that own the models we seed. May be 0 (frontend-only), 1, or many."
|
|
6738
7182
|
),
|
|
6739
|
-
ignore:
|
|
7183
|
+
ignore: z36.array(IgnoreEntry).describe("Directories judged irrelevant to this app's tests, so later steps skip them.")
|
|
6740
7184
|
});
|
|
6741
7185
|
PROJECT_MAP_FILE = "project-map.json";
|
|
6742
7186
|
}
|
|
@@ -6747,11 +7191,11 @@ var parse_entity_audit_exports = {};
|
|
|
6747
7191
|
__export(parse_entity_audit_exports, {
|
|
6748
7192
|
parseEntityNames: () => parseEntityNames
|
|
6749
7193
|
});
|
|
6750
|
-
import { readFile as
|
|
6751
|
-
import { join as
|
|
7194
|
+
import { readFile as readFile9 } from "fs/promises";
|
|
7195
|
+
import { join as join17 } from "path";
|
|
6752
7196
|
async function parseEntityNames(outputDir) {
|
|
6753
7197
|
try {
|
|
6754
|
-
const content = await
|
|
7198
|
+
const content = await readFile9(join17(outputDir, "entity-audit.md"), "utf-8");
|
|
6755
7199
|
const names = [];
|
|
6756
7200
|
for (const line of content.split("\n")) {
|
|
6757
7201
|
const match = line.match(/^\s+-\s+name:\s+(.+)$/);
|
|
@@ -6786,7 +7230,7 @@ var init_count_generated_tests = __esm({
|
|
|
6786
7230
|
});
|
|
6787
7231
|
|
|
6788
7232
|
// src/core/display.ts
|
|
6789
|
-
import { join as
|
|
7233
|
+
import { join as join18 } from "path";
|
|
6790
7234
|
function formatArgs(input, keys) {
|
|
6791
7235
|
const parts = [];
|
|
6792
7236
|
for (const key of keys) {
|
|
@@ -6878,7 +7322,7 @@ function logToStore(store, agentId, info, stats) {
|
|
|
6878
7322
|
const folder = String(tc.input.folder ?? "");
|
|
6879
7323
|
const filename = String(tc.input.filename ?? "");
|
|
6880
7324
|
if (folder && filename) {
|
|
6881
|
-
store.noteWrite(
|
|
7325
|
+
store.noteWrite(join18(TESTS_DIR, folder, normalizeTestFilename(filename)));
|
|
6882
7326
|
}
|
|
6883
7327
|
}
|
|
6884
7328
|
if (tc.name === "bash") {
|
|
@@ -7186,8 +7630,8 @@ var init_agent = __esm({
|
|
|
7186
7630
|
});
|
|
7187
7631
|
|
|
7188
7632
|
// src/core/gitignore.ts
|
|
7189
|
-
import { readFile as
|
|
7190
|
-
import { join as
|
|
7633
|
+
import { readFile as readFile10 } from "fs/promises";
|
|
7634
|
+
import { join as join19, relative as relative2 } from "path";
|
|
7191
7635
|
import { glob as glob2 } from "glob";
|
|
7192
7636
|
async function loadGitignorePatterns(projectRoot) {
|
|
7193
7637
|
const patterns = [
|
|
@@ -7207,10 +7651,10 @@ async function loadGitignorePatterns(projectRoot) {
|
|
|
7207
7651
|
];
|
|
7208
7652
|
const matches = await glob2("**/.gitignore", { cwd: projectRoot, dot: true });
|
|
7209
7653
|
for (const match of matches) {
|
|
7210
|
-
const fullPath =
|
|
7654
|
+
const fullPath = join19(projectRoot, match);
|
|
7211
7655
|
try {
|
|
7212
|
-
const content = await
|
|
7213
|
-
const prefix = relative2(projectRoot,
|
|
7656
|
+
const content = await readFile10(fullPath, "utf-8");
|
|
7657
|
+
const prefix = relative2(projectRoot, join19(projectRoot, match, ".."));
|
|
7214
7658
|
const parsed = parseGitignore(content, prefix);
|
|
7215
7659
|
patterns.push(...parsed);
|
|
7216
7660
|
} catch (err) {
|
|
@@ -7273,7 +7717,7 @@ var init_exec_error = __esm({
|
|
|
7273
7717
|
import { execFile as execFile3 } from "child_process";
|
|
7274
7718
|
import { promisify as promisify2 } from "util";
|
|
7275
7719
|
import { tool } from "ai";
|
|
7276
|
-
import { z as
|
|
7720
|
+
import { z as z37 } from "zod";
|
|
7277
7721
|
function validateCommand(command, allowed) {
|
|
7278
7722
|
const trimmed = command.trim();
|
|
7279
7723
|
if (trimmed.length === 0) return "Empty command";
|
|
@@ -7339,8 +7783,8 @@ var init_bash = __esm({
|
|
|
7339
7783
|
DEFAULT_ALLOWED = /* @__PURE__ */ new Set(["git", "wc", "sort", "head", "tail", "cat", "ls", "find", "diff", "echo"]);
|
|
7340
7784
|
TIMEOUT_MS = 3e4;
|
|
7341
7785
|
MAX_OUTPUT_BYTES = 1024 * 512;
|
|
7342
|
-
inputSchema =
|
|
7343
|
-
command:
|
|
7786
|
+
inputSchema = z37.object({
|
|
7787
|
+
command: z37.string().describe("Shell command to execute")
|
|
7344
7788
|
});
|
|
7345
7789
|
}
|
|
7346
7790
|
});
|
|
@@ -7348,7 +7792,7 @@ var init_bash = __esm({
|
|
|
7348
7792
|
// src/tools/glob.ts
|
|
7349
7793
|
import { tool as tool2 } from "ai";
|
|
7350
7794
|
import { glob as glob3 } from "glob";
|
|
7351
|
-
import { z as
|
|
7795
|
+
import { z as z38 } from "zod";
|
|
7352
7796
|
async function executeGlob(pattern, cwd, ignorePatterns = DEFAULT_IGNORE) {
|
|
7353
7797
|
try {
|
|
7354
7798
|
const matches = await glob3(pattern, {
|
|
@@ -7382,9 +7826,9 @@ var init_glob = __esm({
|
|
|
7382
7826
|
"src/tools/glob.ts"() {
|
|
7383
7827
|
"use strict";
|
|
7384
7828
|
init_esm_shims();
|
|
7385
|
-
inputSchema2 =
|
|
7386
|
-
pattern:
|
|
7387
|
-
cwd:
|
|
7829
|
+
inputSchema2 = z38.object({
|
|
7830
|
+
pattern: z38.string().describe("Glob pattern to match files (e.g. '**/*.ts', 'src/**/*.py')"),
|
|
7831
|
+
cwd: z38.string().optional().describe("Directory to search in. Defaults to working directory.")
|
|
7388
7832
|
});
|
|
7389
7833
|
DEFAULT_IGNORE = [
|
|
7390
7834
|
"**/node_modules/**",
|
|
@@ -7402,7 +7846,7 @@ var init_glob = __esm({
|
|
|
7402
7846
|
import { execFile as execFile4 } from "child_process";
|
|
7403
7847
|
import { promisify as promisify3 } from "util";
|
|
7404
7848
|
import { tool as tool3 } from "ai";
|
|
7405
|
-
import { z as
|
|
7849
|
+
import { z as z39 } from "zod";
|
|
7406
7850
|
function buildGrepTool(workingDirectory) {
|
|
7407
7851
|
return tool3({
|
|
7408
7852
|
description: "Search file contents with ripgrep. Returns matching lines with file paths and line numbers.",
|
|
@@ -7446,10 +7890,10 @@ var init_grep = __esm({
|
|
|
7446
7890
|
init_esm_shims();
|
|
7447
7891
|
init_exec_error();
|
|
7448
7892
|
execFileAsync3 = promisify3(execFile4);
|
|
7449
|
-
inputSchema3 =
|
|
7450
|
-
pattern:
|
|
7451
|
-
glob:
|
|
7452
|
-
path:
|
|
7893
|
+
inputSchema3 = z39.object({
|
|
7894
|
+
pattern: z39.string().describe("Regex pattern to search for in file contents"),
|
|
7895
|
+
glob: z39.string().optional().describe("Glob to filter files (e.g. '*.ts')"),
|
|
7896
|
+
path: z39.string().optional().describe("File or directory to search in")
|
|
7453
7897
|
});
|
|
7454
7898
|
}
|
|
7455
7899
|
});
|
|
@@ -7457,10 +7901,10 @@ var init_grep = __esm({
|
|
|
7457
7901
|
// src/tools/list-directory.ts
|
|
7458
7902
|
import { readdir } from "fs/promises";
|
|
7459
7903
|
import { stat } from "fs/promises";
|
|
7460
|
-
import { join as
|
|
7904
|
+
import { join as join20, relative as relative3 } from "path";
|
|
7461
7905
|
import { tool as tool4 } from "ai";
|
|
7462
7906
|
import { minimatch } from "minimatch";
|
|
7463
|
-
import { z as
|
|
7907
|
+
import { z as z40 } from "zod";
|
|
7464
7908
|
function buildMatcher(patterns) {
|
|
7465
7909
|
const positive = patterns.filter((p) => !p.startsWith("!"));
|
|
7466
7910
|
const negative = patterns.filter((p) => p.startsWith("!")).map((p) => p.slice(1));
|
|
@@ -7482,7 +7926,7 @@ async function buildTree(dirPath, maxDepth, currentDepth, isIgnored, relativeBas
|
|
|
7482
7926
|
const withTypes = [];
|
|
7483
7927
|
for (const name of rawEntries) {
|
|
7484
7928
|
try {
|
|
7485
|
-
const s = await stat(
|
|
7929
|
+
const s = await stat(join20(dirPath, name));
|
|
7486
7930
|
withTypes.push({ name, isDir: s.isDirectory() });
|
|
7487
7931
|
} catch {
|
|
7488
7932
|
withTypes.push({ name, isDir: false });
|
|
@@ -7502,7 +7946,7 @@ async function buildTree(dirPath, maxDepth, currentDepth, isIgnored, relativeBas
|
|
|
7502
7946
|
}
|
|
7503
7947
|
if (entry.isDir) {
|
|
7504
7948
|
const children = await buildTree(
|
|
7505
|
-
|
|
7949
|
+
join20(dirPath, entry.name),
|
|
7506
7950
|
maxDepth,
|
|
7507
7951
|
currentDepth + 1,
|
|
7508
7952
|
isIgnored,
|
|
@@ -7540,10 +7984,10 @@ async function buildListDirectoryTool(workingDirectory) {
|
|
|
7540
7984
|
const isIgnored = buildMatcher(patterns);
|
|
7541
7985
|
return tool4({
|
|
7542
7986
|
description: "List directory structure as a tree. Use this for an overview of the project layout. Start at the root (path='.') with depth 3, then increase depth or narrow path if needed. Do NOT call this on every subdirectory - use glob to find specific files instead. Returns cached result if the same path+depth was already requested.",
|
|
7543
|
-
inputSchema:
|
|
7544
|
-
path:
|
|
7545
|
-
depth:
|
|
7546
|
-
gitignore:
|
|
7987
|
+
inputSchema: z40.object({
|
|
7988
|
+
path: z40.string().default(".").describe("Directory path relative to project root. Defaults to root."),
|
|
7989
|
+
depth: z40.number().min(1).max(15).default(10).describe("Max depth to traverse (1-15). Default 10."),
|
|
7990
|
+
gitignore: z40.boolean().describe(
|
|
7547
7991
|
"Whether to respect the gitignore or to ignore it. true will respect it. false will ignore it. Default true"
|
|
7548
7992
|
).default(true)
|
|
7549
7993
|
}),
|
|
@@ -7555,7 +7999,7 @@ async function buildListDirectoryTool(workingDirectory) {
|
|
|
7555
7999
|
};
|
|
7556
8000
|
}
|
|
7557
8001
|
seen.add(cacheKey);
|
|
7558
|
-
const targetDir = input.path === "." ? workingDirectory :
|
|
8002
|
+
const targetDir = input.path === "." ? workingDirectory : join20(workingDirectory, input.path);
|
|
7559
8003
|
try {
|
|
7560
8004
|
const s = await stat(targetDir);
|
|
7561
8005
|
if (!s.isDirectory()) {
|
|
@@ -7591,10 +8035,10 @@ var init_list_directory = __esm({
|
|
|
7591
8035
|
});
|
|
7592
8036
|
|
|
7593
8037
|
// src/tools/read-file.ts
|
|
7594
|
-
import { readFile as
|
|
8038
|
+
import { readFile as readFile11 } from "fs/promises";
|
|
7595
8039
|
import { relative as relative4, resolve as resolve2 } from "path";
|
|
7596
8040
|
import { tool as tool5 } from "ai";
|
|
7597
|
-
import { z as
|
|
8041
|
+
import { z as z41 } from "zod";
|
|
7598
8042
|
function resolveSandboxedPath(workingDirectory, filePath) {
|
|
7599
8043
|
const absolutePath = resolve2(workingDirectory, filePath);
|
|
7600
8044
|
const relativePath = relative4(workingDirectory, absolutePath);
|
|
@@ -7619,7 +8063,7 @@ async function executeReadFile(workingDirectory, filePath, offset, limit) {
|
|
|
7619
8063
|
const resolved2 = resolveSandboxedPath(workingDirectory, filePath);
|
|
7620
8064
|
if ("error" in resolved2) return resolved2;
|
|
7621
8065
|
try {
|
|
7622
|
-
const content = await
|
|
8066
|
+
const content = await readFile11(resolved2.absolutePath, "utf-8");
|
|
7623
8067
|
const sliced = sliceLines(content, offset ?? 0, limit ?? MAX_LINES);
|
|
7624
8068
|
if (sliced.numbered.length > MAX_OUTPUT_BYTES2) {
|
|
7625
8069
|
sliced.numbered = sliced.numbered.slice(0, MAX_OUTPUT_BYTES2) + `
|
|
@@ -7652,10 +8096,10 @@ var init_read_file = __esm({
|
|
|
7652
8096
|
init_esm_shims();
|
|
7653
8097
|
MAX_LINES = 2e3;
|
|
7654
8098
|
MAX_OUTPUT_BYTES2 = 256 * 1024;
|
|
7655
|
-
inputSchema4 =
|
|
7656
|
-
filePath:
|
|
7657
|
-
offset:
|
|
7658
|
-
limit:
|
|
8099
|
+
inputSchema4 = z41.object({
|
|
8100
|
+
filePath: z41.string().describe("Path to the file (absolute or relative to working directory)"),
|
|
8101
|
+
offset: z41.number().int().min(0).optional().describe("Line number to start reading from (0-based)"),
|
|
8102
|
+
limit: z41.number().int().min(1).optional().describe("Maximum number of lines to read")
|
|
7659
8103
|
});
|
|
7660
8104
|
}
|
|
7661
8105
|
});
|
|
@@ -7678,10 +8122,10 @@ var init_pick_string = __esm({
|
|
|
7678
8122
|
|
|
7679
8123
|
// src/tools/subagent.ts
|
|
7680
8124
|
import { ToolLoopAgent as ToolLoopAgent2, hasToolCall as hasToolCall2, stepCountIs as stepCountIs2, tool as tool6 } from "ai";
|
|
7681
|
-
import { z as
|
|
8125
|
+
import { z as z42 } from "zod";
|
|
7682
8126
|
function buildSubagentTools(workingDirectory, onFileRead) {
|
|
7683
8127
|
const baseReadFile = buildReadFileTool(workingDirectory);
|
|
7684
|
-
const
|
|
8128
|
+
const readFile27 = onFileRead ? tool6({
|
|
7685
8129
|
description: baseReadFile.description,
|
|
7686
8130
|
inputSchema: baseReadFile.inputSchema,
|
|
7687
8131
|
execute: async (input, options) => {
|
|
@@ -7694,7 +8138,7 @@ function buildSubagentTools(workingDirectory, onFileRead) {
|
|
|
7694
8138
|
bash: buildBashTool(workingDirectory),
|
|
7695
8139
|
glob: buildGlobTool(workingDirectory),
|
|
7696
8140
|
grep: buildGrepTool(workingDirectory),
|
|
7697
|
-
read_file:
|
|
8141
|
+
read_file: readFile27
|
|
7698
8142
|
};
|
|
7699
8143
|
}
|
|
7700
8144
|
function buildSubagentTool(model, workingDirectory, onHeartbeat, onFileRead) {
|
|
@@ -7751,11 +8195,11 @@ var init_subagent = __esm({
|
|
|
7751
8195
|
init_glob();
|
|
7752
8196
|
init_grep();
|
|
7753
8197
|
init_read_file();
|
|
7754
|
-
inputSchema5 =
|
|
7755
|
-
instruction:
|
|
8198
|
+
inputSchema5 = z42.object({
|
|
8199
|
+
instruction: z42.string().describe("Focused task for the subagent. Be specific about files and patterns to investigate.")
|
|
7756
8200
|
});
|
|
7757
|
-
resultSchema =
|
|
7758
|
-
findings:
|
|
8201
|
+
resultSchema = z42.object({
|
|
8202
|
+
findings: z42.string().describe("Summary of what was found")
|
|
7759
8203
|
});
|
|
7760
8204
|
SYSTEM_PROMPT = `You are a code research assistant. You have tools to explore a codebase: bash (shell commands, mainly git), glob (find files), grep (search content), and read_file (read files).
|
|
7761
8205
|
|
|
@@ -7766,10 +8210,10 @@ Be thorough but focused - only investigate what's relevant to your instruction.`
|
|
|
7766
8210
|
});
|
|
7767
8211
|
|
|
7768
8212
|
// src/tools/write-file.ts
|
|
7769
|
-
import { writeFile as
|
|
8213
|
+
import { writeFile as writeFile7, mkdir as mkdir3 } from "fs/promises";
|
|
7770
8214
|
import { dirname as dirname3, relative as relative5, resolve as resolve3 } from "path";
|
|
7771
8215
|
import { tool as tool7 } from "ai";
|
|
7772
|
-
import { z as
|
|
8216
|
+
import { z as z43 } from "zod";
|
|
7773
8217
|
async function executeWriteFile(outputDirectory, filePath, content) {
|
|
7774
8218
|
const cleaned = filePath.replace(/^autonoma\//, "");
|
|
7775
8219
|
const absolutePath = resolve3(outputDirectory, cleaned);
|
|
@@ -7779,7 +8223,7 @@ async function executeWriteFile(outputDirectory, filePath, content) {
|
|
|
7779
8223
|
}
|
|
7780
8224
|
try {
|
|
7781
8225
|
await mkdir3(dirname3(absolutePath), { recursive: true });
|
|
7782
|
-
await
|
|
8226
|
+
await writeFile7(absolutePath, content, "utf-8");
|
|
7783
8227
|
return { path: relativePath, bytesWritten: content.length };
|
|
7784
8228
|
} catch (err) {
|
|
7785
8229
|
const message = err instanceof Error ? err.message : String(err);
|
|
@@ -7798,21 +8242,21 @@ var init_write_file = __esm({
|
|
|
7798
8242
|
"src/tools/write-file.ts"() {
|
|
7799
8243
|
"use strict";
|
|
7800
8244
|
init_esm_shims();
|
|
7801
|
-
inputSchema6 =
|
|
7802
|
-
filePath:
|
|
7803
|
-
content:
|
|
8245
|
+
inputSchema6 = z43.object({
|
|
8246
|
+
filePath: z43.string().describe("Path to write (absolute or relative to output directory)"),
|
|
8247
|
+
content: z43.string().describe("File content to write")
|
|
7804
8248
|
});
|
|
7805
8249
|
}
|
|
7806
8250
|
});
|
|
7807
8251
|
|
|
7808
8252
|
// src/tools/ask-user.ts
|
|
7809
8253
|
import { tool as tool8 } from "ai";
|
|
7810
|
-
import { z as
|
|
8254
|
+
import { z as z44 } from "zod";
|
|
7811
8255
|
function buildAskUserTool() {
|
|
7812
8256
|
return tool8({
|
|
7813
8257
|
description: "Ask the user a question ONLY when the answer is truly unknowable from the codebase. Valid reasons: untyped JSON/JSONB field schemas, business rules not in code, config values not in source. NEVER ask about: field names (read the schema), field types (read the ORM model), enum values (read the code), relationships (read foreign keys), numeric values (read the seed data or defaults). If you can find it by reading a file, DO NOT ask - read the file instead.",
|
|
7814
|
-
inputSchema:
|
|
7815
|
-
question:
|
|
8258
|
+
inputSchema: z44.object({
|
|
8259
|
+
question: z44.string().describe(
|
|
7816
8260
|
"A clear, plain-language question. State exactly what you need to know and why you can't find it in code. BAD: 'What are the decimal values for checking_balance?' - GOOD: 'Your Account model has a metadata JSON column with no type definition. What fields go inside it?'"
|
|
7817
8261
|
)
|
|
7818
8262
|
}),
|
|
@@ -7872,7 +8316,7 @@ var init_tools = __esm({
|
|
|
7872
8316
|
|
|
7873
8317
|
// src/agents/00-project-mapper/coverage.ts
|
|
7874
8318
|
import { readdir as readdir2 } from "fs/promises";
|
|
7875
|
-
import { join as
|
|
8319
|
+
import { join as join21 } from "path";
|
|
7876
8320
|
async function findUncoveredDirs(projectRoot, map) {
|
|
7877
8321
|
const entries = [...map.frontends, ...map.backends, ...map.ignore].map(
|
|
7878
8322
|
(e) => e.path.replace(/^\.\//, "").replace(/\/+$/, "")
|
|
@@ -7896,7 +8340,7 @@ async function findUncoveredDirs(projectRoot, map) {
|
|
|
7896
8340
|
const childRel = rel === "" ? child.name : `${rel}/${child.name}`;
|
|
7897
8341
|
if (underEntry(childRel)) continue;
|
|
7898
8342
|
if (hasEntryWithin(childRel)) {
|
|
7899
|
-
if (depth + 1 < MAX_DEPTH) await walk(
|
|
8343
|
+
if (depth + 1 < MAX_DEPTH) await walk(join21(abs, child.name), childRel, depth + 1);
|
|
7900
8344
|
} else {
|
|
7901
8345
|
out.push(childRel);
|
|
7902
8346
|
}
|
|
@@ -7923,7 +8367,7 @@ __export(project_mapper_exports, {
|
|
|
7923
8367
|
runProjectMapper: () => runProjectMapper
|
|
7924
8368
|
});
|
|
7925
8369
|
import { tool as tool9 } from "ai";
|
|
7926
|
-
import { z as
|
|
8370
|
+
import { z as z45 } from "zod";
|
|
7927
8371
|
async function runProjectMapper(input) {
|
|
7928
8372
|
const model = getModel(input.modelId);
|
|
7929
8373
|
const { logger, onStepFinish } = buildDefaultStepLogger("project-map", MAX_STEPS);
|
|
@@ -7957,7 +8401,7 @@ First build the full inventory (workspace/package manifests + top-level director
|
|
|
7957
8401
|
}),
|
|
7958
8402
|
finish: tool9({
|
|
7959
8403
|
description: "End this step. Call once set_project_map has recorded a complete map.",
|
|
7960
|
-
inputSchema:
|
|
8404
|
+
inputSchema: z45.object({}),
|
|
7961
8405
|
execute: () => {
|
|
7962
8406
|
if (captured == null) {
|
|
7963
8407
|
return { error: "Cannot finish: no map recorded - call set_project_map first." };
|
|
@@ -8001,7 +8445,7 @@ __export(pages_finder_exports, {
|
|
|
8001
8445
|
import { existsSync } from "fs";
|
|
8002
8446
|
import * as path2 from "path";
|
|
8003
8447
|
import { tool as tool10 } from "ai";
|
|
8004
|
-
import { z as
|
|
8448
|
+
import { z as z46 } from "zod";
|
|
8005
8449
|
async function runPageFinder(input) {
|
|
8006
8450
|
const model = getModel(input.modelId);
|
|
8007
8451
|
const pageCollector = new PageCollector();
|
|
@@ -8036,12 +8480,12 @@ ${input.extraMessage}`;
|
|
|
8036
8480
|
}),
|
|
8037
8481
|
view_pages: tool10({
|
|
8038
8482
|
description: "use this tool to view all the pages that you already added",
|
|
8039
|
-
inputSchema:
|
|
8483
|
+
inputSchema: z46.object(),
|
|
8040
8484
|
execute: () => pageCollector.viewPages()
|
|
8041
8485
|
}),
|
|
8042
8486
|
finish: tool10({
|
|
8043
8487
|
description: "End this step. Call once every page in the codebase has been added via add_page.",
|
|
8044
|
-
inputSchema:
|
|
8488
|
+
inputSchema: z46.object({}),
|
|
8045
8489
|
execute: () => {
|
|
8046
8490
|
if (pageCollector.pages.size === 0) {
|
|
8047
8491
|
return {
|
|
@@ -8068,10 +8512,10 @@ var init_pages_finder = __esm({
|
|
|
8068
8512
|
init_agent();
|
|
8069
8513
|
init_model();
|
|
8070
8514
|
init_tools();
|
|
8071
|
-
Page =
|
|
8072
|
-
route:
|
|
8073
|
-
path:
|
|
8074
|
-
description:
|
|
8515
|
+
Page = z46.object({
|
|
8516
|
+
route: z46.string().min(1),
|
|
8517
|
+
path: z46.string().min(1),
|
|
8518
|
+
description: z46.string().min(10)
|
|
8075
8519
|
});
|
|
8076
8520
|
PageCollector = class {
|
|
8077
8521
|
// the key is the path
|
|
@@ -8267,15 +8711,15 @@ var kb_generator_exports = {};
|
|
|
8267
8711
|
__export(kb_generator_exports, {
|
|
8268
8712
|
runKBGenerator: () => runKBGenerator
|
|
8269
8713
|
});
|
|
8270
|
-
import { readFile as
|
|
8271
|
-
import { join as
|
|
8714
|
+
import { readFile as readFile12 } from "fs/promises";
|
|
8715
|
+
import { join as join22, resolve as resolve5 } from "path";
|
|
8272
8716
|
import { tool as tool11 } from "ai";
|
|
8273
|
-
import { z as
|
|
8717
|
+
import { z as z47 } from "zod";
|
|
8274
8718
|
function buildRegisterPagesTool(tracker) {
|
|
8275
8719
|
return tool11({
|
|
8276
8720
|
description: "Register ALL page/route files discovered via glob. Call this ONCE after globbing for page files. The system will track which ones you've read and block finish until all are covered.",
|
|
8277
|
-
inputSchema:
|
|
8278
|
-
pages:
|
|
8721
|
+
inputSchema: z47.object({
|
|
8722
|
+
pages: z47.array(z47.string()).describe("All page file paths found by glob")
|
|
8279
8723
|
}),
|
|
8280
8724
|
execute: async (input) => {
|
|
8281
8725
|
tracker.register(input.pages);
|
|
@@ -8289,7 +8733,7 @@ function buildRegisterPagesTool(tracker) {
|
|
|
8289
8733
|
function buildPageCoverageTool(tracker) {
|
|
8290
8734
|
return tool11({
|
|
8291
8735
|
description: "Check how many registered pages you've read vs how many remain.",
|
|
8292
|
-
inputSchema:
|
|
8736
|
+
inputSchema: z47.object({}),
|
|
8293
8737
|
execute: async () => tracker.coverage()
|
|
8294
8738
|
});
|
|
8295
8739
|
}
|
|
@@ -8300,9 +8744,9 @@ function requiredReads(total) {
|
|
|
8300
8744
|
function buildFinishTool(tracker, onFinish) {
|
|
8301
8745
|
return tool11({
|
|
8302
8746
|
description: "Call when you have finished generating the knowledge base. BLOCKED until you have read enough of the registered routes (every route on a small app; a strong majority on a large one) - call page_coverage first to check how many remain.",
|
|
8303
|
-
inputSchema:
|
|
8304
|
-
summary:
|
|
8305
|
-
artifacts:
|
|
8747
|
+
inputSchema: z47.object({
|
|
8748
|
+
summary: z47.string().describe("Summary of what was generated"),
|
|
8749
|
+
artifacts: z47.array(z47.string()).describe("List of files written")
|
|
8306
8750
|
}),
|
|
8307
8751
|
execute: async (input) => {
|
|
8308
8752
|
const cov = tracker.coverage();
|
|
@@ -8381,8 +8825,8 @@ Output files:
|
|
|
8381
8825
|
const agentConfig = buildKbAgentConfig(tracker, model, input, onStepFinish, setResult);
|
|
8382
8826
|
await runAgent(agentConfig, prompt, () => result);
|
|
8383
8827
|
logger.summary();
|
|
8384
|
-
const autonomaPath =
|
|
8385
|
-
const autonomaExists = await
|
|
8828
|
+
const autonomaPath = join22(input.outputDir, "AUTONOMA.md");
|
|
8829
|
+
const autonomaExists = await readFile12(autonomaPath, "utf-8").then(() => true).catch((err) => {
|
|
8386
8830
|
debugLog("AUTONOMA.md not found while checking step completion", { err });
|
|
8387
8831
|
return false;
|
|
8388
8832
|
});
|
|
@@ -8568,17 +9012,17 @@ var entity_audit_exports = {};
|
|
|
8568
9012
|
__export(entity_audit_exports, {
|
|
8569
9013
|
runEntityAudit: () => runEntityAudit
|
|
8570
9014
|
});
|
|
8571
|
-
import { readFile as
|
|
8572
|
-
import { join as
|
|
9015
|
+
import { readFile as readFile13, writeFile as writeFile8 } from "fs/promises";
|
|
9016
|
+
import { join as join23 } from "path";
|
|
8573
9017
|
import { tool as tool12 } from "ai";
|
|
8574
9018
|
import { glob as glob4 } from "glob";
|
|
8575
|
-
import { z as
|
|
9019
|
+
import { z as z48 } from "zod";
|
|
8576
9020
|
function buildRegisterModelsTool(tracker) {
|
|
8577
9021
|
return tool12({
|
|
8578
9022
|
description: "Register ALL database models discovered via grep. Call this ONCE after grepping for model definitions. After registering, use next_model to process them one at a time.",
|
|
8579
|
-
inputSchema:
|
|
8580
|
-
models:
|
|
8581
|
-
framework:
|
|
9023
|
+
inputSchema: z48.object({
|
|
9024
|
+
models: z48.array(z48.string()).describe("All model/table names found by grep"),
|
|
9025
|
+
framework: z48.string().describe("Database framework detected (e.g. 'sqlalchemy', 'prisma', 'drizzle')")
|
|
8582
9026
|
}),
|
|
8583
9027
|
execute: async (input) => {
|
|
8584
9028
|
tracker.register(input.models);
|
|
@@ -8594,7 +9038,7 @@ function buildRegisterModelsTool(tracker) {
|
|
|
8594
9038
|
function buildNextModelTool(tracker) {
|
|
8595
9039
|
return tool12({
|
|
8596
9040
|
description: "Get the next model to audit from the queue. If you called next_model before without calling mark_model_audited, the previous model is auto-skipped (marked as no creation path found). Returns done:true when all models are processed.",
|
|
8597
|
-
inputSchema:
|
|
9041
|
+
inputSchema: z48.object({}),
|
|
8598
9042
|
execute: async () => {
|
|
8599
9043
|
const next = tracker.nextModel();
|
|
8600
9044
|
if (!next) {
|
|
@@ -8614,17 +9058,17 @@ function buildNextModelTool(tracker) {
|
|
|
8614
9058
|
function buildMarkModelAuditedTool(tracker) {
|
|
8615
9059
|
return tool12({
|
|
8616
9060
|
description: "Mark a model as audited after you have determined its creation paths. Call this for EACH model after reading its creation code and determining independently_created + created_by. Include creation_function (e.g. 'UserService.create'), side_effects (list of things the creation does beyond the model itself), and for each created_by entry include owner, via (function name), and why (one sentence explaining the relationship).",
|
|
8617
|
-
inputSchema:
|
|
8618
|
-
model:
|
|
8619
|
-
independently_created:
|
|
8620
|
-
creation_file:
|
|
8621
|
-
creation_function:
|
|
8622
|
-
side_effects:
|
|
8623
|
-
created_by:
|
|
8624
|
-
|
|
8625
|
-
owner:
|
|
8626
|
-
via:
|
|
8627
|
-
why:
|
|
9061
|
+
inputSchema: z48.object({
|
|
9062
|
+
model: z48.string().describe("Model name"),
|
|
9063
|
+
independently_created: z48.boolean(),
|
|
9064
|
+
creation_file: z48.string().optional().describe("File containing the creation function"),
|
|
9065
|
+
creation_function: z48.string().optional().describe("Function/method name (e.g. 'UserService.create' or 'create_user')"),
|
|
9066
|
+
side_effects: z48.array(z48.string()).optional().describe("Side effects of creation (e.g. 'creates default Settings row', 'hashes password')"),
|
|
9067
|
+
created_by: z48.array(
|
|
9068
|
+
z48.object({
|
|
9069
|
+
owner: z48.string().describe("Owner model name"),
|
|
9070
|
+
via: z48.string().optional().describe("Function that creates this model (e.g. 'OrganizationService.create')"),
|
|
9071
|
+
why: z48.string().optional().describe("One sentence explaining why this model is created as a side effect")
|
|
8628
9072
|
})
|
|
8629
9073
|
).describe("List of owner models that create this as a side effect, empty array if none")
|
|
8630
9074
|
}),
|
|
@@ -8663,16 +9107,16 @@ function buildMarkModelAuditedTool(tracker) {
|
|
|
8663
9107
|
function buildModelCoverageTool(tracker) {
|
|
8664
9108
|
return tool12({
|
|
8665
9109
|
description: "Check how many registered models you've audited vs how many remain.",
|
|
8666
|
-
inputSchema:
|
|
9110
|
+
inputSchema: z48.object({}),
|
|
8667
9111
|
execute: async () => tracker.coverage()
|
|
8668
9112
|
});
|
|
8669
9113
|
}
|
|
8670
9114
|
function buildFinishTool2(tracker, onFinish) {
|
|
8671
9115
|
return tool12({
|
|
8672
9116
|
description: "Call when entity audit is complete. BLOCKED if there are unaudited models - call model_coverage first to check.",
|
|
8673
|
-
inputSchema:
|
|
8674
|
-
summary:
|
|
8675
|
-
artifacts:
|
|
9117
|
+
inputSchema: z48.object({
|
|
9118
|
+
summary: z48.string().describe("Summary of the audit"),
|
|
9119
|
+
artifacts: z48.array(z48.string()).describe("Files written")
|
|
8676
9120
|
}),
|
|
8677
9121
|
execute: async (input) => {
|
|
8678
9122
|
const cov = tracker.coverage();
|
|
@@ -8700,7 +9144,7 @@ async function findPrismaSchema(projectRoot) {
|
|
|
8700
9144
|
return candidates[0] ?? void 0;
|
|
8701
9145
|
}
|
|
8702
9146
|
async function extractPrismaModels(schemaPath) {
|
|
8703
|
-
const content = await
|
|
9147
|
+
const content = await readFile13(schemaPath, "utf-8");
|
|
8704
9148
|
return content.split("\n").filter((line) => line.startsWith("model ")).map((line) => line.split(/\s+/)[1]).filter((name) => name != null);
|
|
8705
9149
|
}
|
|
8706
9150
|
async function detectFrameworkAndModels(projectRoot) {
|
|
@@ -8787,8 +9231,8 @@ ${formatException(err)}`);
|
|
|
8787
9231
|
logger.summary();
|
|
8788
9232
|
const writeCanonicalAudit = async () => {
|
|
8789
9233
|
if (tracker.auditedModels.size === 0) return void 0;
|
|
8790
|
-
const auditPath =
|
|
8791
|
-
await
|
|
9234
|
+
const auditPath = join23(input.outputDir, "entity-audit.md");
|
|
9235
|
+
await writeFile8(auditPath, tracker.generateAuditMarkdown(), "utf-8");
|
|
8792
9236
|
return auditPath;
|
|
8793
9237
|
};
|
|
8794
9238
|
const canonicalPath = await writeCanonicalAudit();
|
|
@@ -8802,9 +9246,9 @@ ${formatException(err)}`);
|
|
|
8802
9246
|
}
|
|
8803
9247
|
const reviewed = result;
|
|
8804
9248
|
if (!reviewed) {
|
|
8805
|
-
const auditPath =
|
|
9249
|
+
const auditPath = join23(input.outputDir, "entity-audit.md");
|
|
8806
9250
|
try {
|
|
8807
|
-
await
|
|
9251
|
+
await readFile13(auditPath, "utf-8");
|
|
8808
9252
|
return {
|
|
8809
9253
|
success: true,
|
|
8810
9254
|
artifacts: ["entity-audit.md"],
|
|
@@ -9008,15 +9452,37 @@ anything the database will refuse a second copy of - must embed a token INSIDE a
|
|
|
9008
9452
|
otherwise real-looking value ("admin+{{${TEST_RUN_ID_TOKEN}}}@acme.test",
|
|
9009
9453
|
"acme-{{${TEST_RUN_SHORT_ID_TOKEN}}}"), never replace the value entirely. Two tests
|
|
9010
9454
|
run at the same time seed this scenario twice; without the token the second one fails
|
|
9011
|
-
on a unique constraint.
|
|
9012
|
-
|
|
9455
|
+
on a unique constraint. No other {{token}} or bare {variable} exists - inventing one
|
|
9456
|
+
is rejected.
|
|
9457
|
+
|
|
9458
|
+
## Data the app compares against the current time
|
|
9459
|
+
This scenario is written once and seeded again, unchanged, before every run for as long
|
|
9460
|
+
as it lives. So a value the application compares against the current time is not static
|
|
9461
|
+
data: a record meant to be upcoming, overdue, expiring soon, still valid, or recent is a
|
|
9462
|
+
fact about WHEN THE TEST RUNS. Written as a calendar date it is right on the day you
|
|
9463
|
+
write it and quietly wrong ever after, and the failure it causes reads as a broken
|
|
9464
|
+
product rather than as stale data.
|
|
9465
|
+
|
|
9466
|
+
For every date or time you write, ask one question: does the app branch on this being
|
|
9467
|
+
before or after now? Look for the answer in its queries - a list split into current and
|
|
9468
|
+
past, a filter against an expiry, a state derived from a deadline. Do not guess from the
|
|
9469
|
+
field name.
|
|
9470
|
+
|
|
9471
|
+
Where the answer is yes, write the value as an offset from the moment of seeding
|
|
9472
|
+
("starts 3 days after seeding, at 10:00", "ended 2 weeks before seeding") rather than a
|
|
9473
|
+
date. Where it is no - a recurring weekly schedule, a date of birth, a timestamp nothing
|
|
9474
|
+
filters on - a concrete date is correct and an offset would only obscure it.
|
|
9475
|
+
|
|
9476
|
+
An offset is prose in your table, not a token: there is no {{token}} for time, and
|
|
9477
|
+
inventing one is rejected like any other. The recipe builder turns what you write into a
|
|
9478
|
+
value its factories compute at seeding time.`;
|
|
9013
9479
|
}
|
|
9014
9480
|
});
|
|
9015
9481
|
|
|
9016
9482
|
// src/agents/03-scenario-recipe/scenario-table.ts
|
|
9017
|
-
import { readFile as
|
|
9018
|
-
import { join as
|
|
9019
|
-
import matter2 from "gray-matter";
|
|
9483
|
+
import { readFile as readFile14 } from "fs/promises";
|
|
9484
|
+
import { join as join24 } from "path";
|
|
9485
|
+
import matter2 from "@11ty/gray-matter";
|
|
9020
9486
|
function validateScenarioIsConcrete(content) {
|
|
9021
9487
|
const errors = findUnknownRecipeTokens(content).map(
|
|
9022
9488
|
(token) => `unknown token "{{${token}}}" - scenario values must be concrete. Replace it with the exact static value; the only tokens Autonoma substitutes are ${BUILT_IN_RECIPE_TOKEN_LIST}.`
|
|
@@ -9050,22 +9516,22 @@ __export(scenario_recipe_exports, {
|
|
|
9050
9516
|
feedbackToScenario: () => feedbackToScenario,
|
|
9051
9517
|
runScenarioRecipe: () => runScenarioRecipe
|
|
9052
9518
|
});
|
|
9053
|
-
import { readFile as
|
|
9054
|
-
import { join as
|
|
9519
|
+
import { readFile as readFile15 } from "fs/promises";
|
|
9520
|
+
import { join as join25 } from "path";
|
|
9055
9521
|
import { tool as tool13 } from "ai";
|
|
9056
|
-
import { z as
|
|
9522
|
+
import { z as z49 } from "zod";
|
|
9057
9523
|
function buildFinishTool3(requiredEntities, outputDir, onFinish) {
|
|
9058
9524
|
return tool13({
|
|
9059
9525
|
description: "Call when scenario design is complete and scenarios.md is written. BLOCKED if any required entities are missing from the scenario.",
|
|
9060
|
-
inputSchema:
|
|
9061
|
-
summary:
|
|
9062
|
-
entityCount:
|
|
9063
|
-
artifacts:
|
|
9526
|
+
inputSchema: z49.object({
|
|
9527
|
+
summary: z49.string().describe("Summary of the scenario"),
|
|
9528
|
+
entityCount: z49.number().describe("Number of entity types in the scenario"),
|
|
9529
|
+
artifacts: z49.array(z49.string()).describe("Files written")
|
|
9064
9530
|
}),
|
|
9065
9531
|
execute: async (input) => {
|
|
9066
9532
|
let content;
|
|
9067
9533
|
try {
|
|
9068
|
-
content = await
|
|
9534
|
+
content = await readFile15(join25(outputDir, "scenarios.md"), "utf-8");
|
|
9069
9535
|
} catch {
|
|
9070
9536
|
return { error: "Cannot finish: scenarios.md not found. Write it first." };
|
|
9071
9537
|
}
|
|
@@ -9144,9 +9610,9 @@ When done, call finish.`;
|
|
|
9144
9610
|
logger.summary();
|
|
9145
9611
|
const reviewed = result;
|
|
9146
9612
|
if (!reviewed) {
|
|
9147
|
-
const scenariosPath =
|
|
9613
|
+
const scenariosPath = join25(input.outputDir, "scenarios.md");
|
|
9148
9614
|
try {
|
|
9149
|
-
await
|
|
9615
|
+
await readFile15(scenariosPath, "utf-8");
|
|
9150
9616
|
return {
|
|
9151
9617
|
success: true,
|
|
9152
9618
|
artifacts: ["scenarios.md"],
|
|
@@ -9205,12 +9671,12 @@ var init_scenario_recipe = __esm({
|
|
|
9205
9671
|
});
|
|
9206
9672
|
|
|
9207
9673
|
// src/agents/04-recipe-builder/entity-order.ts
|
|
9208
|
-
import { readFile as
|
|
9209
|
-
import { join as
|
|
9210
|
-
import matter3 from "gray-matter";
|
|
9211
|
-
import { z as
|
|
9674
|
+
import { readFile as readFile16 } from "fs/promises";
|
|
9675
|
+
import { join as join26 } from "path";
|
|
9676
|
+
import matter3 from "@11ty/gray-matter";
|
|
9677
|
+
import { z as z50 } from "zod";
|
|
9212
9678
|
async function parseEntityAudit(outputDir) {
|
|
9213
|
-
const raw = await
|
|
9679
|
+
const raw = await readFile16(join26(outputDir, "entity-audit.md"), "utf-8");
|
|
9214
9680
|
try {
|
|
9215
9681
|
const parsed = frontmatterSchema.safeParse(matter3(raw).data);
|
|
9216
9682
|
if (parsed.success && parsed.data.models.length > 0) {
|
|
@@ -9371,34 +9837,34 @@ var init_entity_order = __esm({
|
|
|
9371
9837
|
"src/agents/04-recipe-builder/entity-order.ts"() {
|
|
9372
9838
|
"use strict";
|
|
9373
9839
|
init_esm_shims();
|
|
9374
|
-
createdBySchema =
|
|
9375
|
-
owner:
|
|
9376
|
-
via:
|
|
9377
|
-
why:
|
|
9840
|
+
createdBySchema = z50.object({
|
|
9841
|
+
owner: z50.string(),
|
|
9842
|
+
via: z50.string().optional(),
|
|
9843
|
+
why: z50.string().optional()
|
|
9378
9844
|
});
|
|
9379
|
-
auditedModelSchema =
|
|
9380
|
-
name:
|
|
9381
|
-
independently_created:
|
|
9382
|
-
creation_file:
|
|
9383
|
-
creation_function:
|
|
9384
|
-
side_effects:
|
|
9845
|
+
auditedModelSchema = z50.object({
|
|
9846
|
+
name: z50.string(),
|
|
9847
|
+
independently_created: z50.coerce.boolean().default(false),
|
|
9848
|
+
creation_file: z50.string().optional(),
|
|
9849
|
+
creation_function: z50.string().optional(),
|
|
9850
|
+
side_effects: z50.array(z50.string()).optional(),
|
|
9385
9851
|
// Tolerate a stray `created_by:` with no entries (parsed as null by YAML).
|
|
9386
|
-
created_by:
|
|
9852
|
+
created_by: z50.array(createdBySchema).nullish().transform((v) => v ?? [])
|
|
9387
9853
|
});
|
|
9388
|
-
frontmatterSchema =
|
|
9389
|
-
models:
|
|
9854
|
+
frontmatterSchema = z50.object({
|
|
9855
|
+
models: z50.array(auditedModelSchema).nullish().transform((v) => v ?? [])
|
|
9390
9856
|
});
|
|
9391
9857
|
}
|
|
9392
9858
|
});
|
|
9393
9859
|
|
|
9394
9860
|
// src/agents/04-recipe-builder/completion.ts
|
|
9395
|
-
import { readFile as
|
|
9396
|
-
import { join as
|
|
9397
|
-
import { z as
|
|
9861
|
+
import { readFile as readFile17 } from "fs/promises";
|
|
9862
|
+
import { join as join27 } from "path";
|
|
9863
|
+
import { z as z51 } from "zod";
|
|
9398
9864
|
async function readCompletion(outputDir) {
|
|
9399
9865
|
let raw;
|
|
9400
9866
|
try {
|
|
9401
|
-
raw = await
|
|
9867
|
+
raw = await readFile17(join27(outputDir, COMPLETION_MARKER_FILE), "utf-8");
|
|
9402
9868
|
} catch (err) {
|
|
9403
9869
|
debugLog("No completion marker yet", { err });
|
|
9404
9870
|
return false;
|
|
@@ -9424,7 +9890,7 @@ var init_completion = __esm({
|
|
|
9424
9890
|
init_esm_shims();
|
|
9425
9891
|
init_debug();
|
|
9426
9892
|
COMPLETION_MARKER_FILE = ".sdk-integration-complete";
|
|
9427
|
-
completionSchema =
|
|
9893
|
+
completionSchema = z51.object({ complete: z51.literal(true) });
|
|
9428
9894
|
}
|
|
9429
9895
|
});
|
|
9430
9896
|
|
|
@@ -9432,9 +9898,10 @@ var init_completion = __esm({
|
|
|
9432
9898
|
function watchForCompletion(outputDir, proc, timing = DEFAULT_WATCH_TIMING) {
|
|
9433
9899
|
let graceTimer;
|
|
9434
9900
|
let killTimer;
|
|
9901
|
+
let stopped = false;
|
|
9435
9902
|
const poll = setInterval(() => {
|
|
9436
9903
|
void readCompletion(outputDir).then((complete) => {
|
|
9437
|
-
if (!complete || graceTimer != null) return;
|
|
9904
|
+
if (stopped || !complete || graceTimer != null) return;
|
|
9438
9905
|
debugLog("Completion marker detected while the agent runs; scheduling terminal reclaim");
|
|
9439
9906
|
clearInterval(poll);
|
|
9440
9907
|
graceTimer = setTimeout(() => {
|
|
@@ -9448,6 +9915,7 @@ function watchForCompletion(outputDir, proc, timing = DEFAULT_WATCH_TIMING) {
|
|
|
9448
9915
|
});
|
|
9449
9916
|
}, timing.pollMs);
|
|
9450
9917
|
return () => {
|
|
9918
|
+
stopped = true;
|
|
9451
9919
|
clearInterval(poll);
|
|
9452
9920
|
if (graceTimer != null) clearTimeout(graceTimer);
|
|
9453
9921
|
if (killTimer != null) clearTimeout(killTimer);
|
|
@@ -9472,8 +9940,8 @@ var init_completion_watch = __esm({
|
|
|
9472
9940
|
});
|
|
9473
9941
|
|
|
9474
9942
|
// src/agents/04-recipe-builder/integration-prompt.ts
|
|
9475
|
-
import { writeFile as
|
|
9476
|
-
import { join as
|
|
9943
|
+
import { writeFile as writeFile9 } from "fs/promises";
|
|
9944
|
+
import { join as join28 } from "path";
|
|
9477
9945
|
function renderIntegrationPrompt(params) {
|
|
9478
9946
|
const priorFailureSection = params.priorFailure != null ? `
|
|
9479
9947
|
\u2550\u2550\u2550 A PRIOR SESSION DID NOT COMPLETE - READ THIS FIRST \u2550\u2550\u2550
|
|
@@ -9556,7 +10024,10 @@ STOP immediately and say EXACTLY what is missing and why you can't proceed.
|
|
|
9556
10024
|
JavaScript/npm package.
|
|
9557
10025
|
2. Implement ONE endpoint (prefer "${DEFAULT_ENDPOINT_PATH}" unless the repo has a
|
|
9558
10026
|
clearly better convention) that handles the discover / up / down protocol through
|
|
9559
|
-
the SDK handler.
|
|
10027
|
+
the SDK handler. If you mount it anywhere OTHER than "${DEFAULT_ENDPOINT_PATH}",
|
|
10028
|
+
say so on its own line in IMPLEMENTATION.md, exactly as
|
|
10029
|
+
"SDK endpoint path: /your/path" - Autonoma assumes the default and the next step
|
|
10030
|
+
of the setup has to be told the real one, or every scenario provision 404s. The signing secret AUTONOMA_SHARED_SECRET is ALREADY provisioned
|
|
9560
10031
|
in the app's environment - verify the x-signature HMAC against that env value (the
|
|
9561
10032
|
SDK reads it for you). Do NOT hardcode a secret or overwrite the env value.
|
|
9562
10033
|
3. Implement a real factory for EVERY entity the entity audit says needs one:
|
|
@@ -9631,6 +10102,35 @@ value still reads as real: "admin+{{${TEST_RUN_ID_TOKEN}}}@acme.test", "acme-{{$
|
|
|
9631
10102
|
Any OTHER {{token}} is rejected on upload: there is no general variable mechanism, so
|
|
9632
10103
|
never invent one.
|
|
9633
10104
|
|
|
10105
|
+
\u2550\u2550\u2550 DATA THE APP COMPARES AGAINST THE CURRENT TIME \u2550\u2550\u2550
|
|
10106
|
+
Autonoma stores this recipe once and seeds it again, UNCHANGED, before every run for
|
|
10107
|
+
months. So a value the application compares against the current time is wrong the day
|
|
10108
|
+
after you write it. A row meant to be upcoming, overdue, expiring soon, still valid, or
|
|
10109
|
+
recent describes WHEN THE TEST RUNS, not a date on a calendar - and when it goes stale
|
|
10110
|
+
the suite fails in a way that reads as a product bug rather than as stale data. A seeded
|
|
10111
|
+
session or credential that has drifted past its expiry breaks every test at once.
|
|
10112
|
+
|
|
10113
|
+
For every date/time field you seed, ask one question: does the app branch on this being
|
|
10114
|
+
before or after now? Find the answer in the app's own queries - a list partitioned into
|
|
10115
|
+
current and past, a filter against an expiry, a state derived from a deadline. Do not
|
|
10116
|
+
guess from the field name.
|
|
10117
|
+
|
|
10118
|
+
Where the answer is YES, do not put an instant in the recipe. Take an OFFSET as the
|
|
10119
|
+
factory's input, and have the factory add it to the current time as it creates the row -
|
|
10120
|
+
the factory runs at seeding time, so its clock is the right one:
|
|
10121
|
+
recipe: { "startsInMinutes": 4320, "durationMinutes": 30 }
|
|
10122
|
+
factory: reads startsInMinutes, writes the column as (now + 4320 minutes)
|
|
10123
|
+
A factory's input schema is YOUR interface and does not have to mirror its columns -
|
|
10124
|
+
converting an input into the column's real shape inside the factory is normal, and is
|
|
10125
|
+
how a factory already handles any column whose stored form differs from its input. Anchor
|
|
10126
|
+
or round the derived value when the app only accepts particular times.
|
|
10127
|
+
|
|
10128
|
+
Where the answer is NO - a recurring weekly schedule, a date of birth, a timestamp
|
|
10129
|
+
nothing filters on - a concrete value is correct and an offset would only obscure it.
|
|
10130
|
+
|
|
10131
|
+
There is no {{token}} for time. Do not invent one: it is rejected on upload exactly like
|
|
10132
|
+
any other unknown token, and the derivation belongs in the factory anyway.
|
|
10133
|
+
|
|
9634
10134
|
WHICH fields need a token is not a judgement call and not a guess from the field name.
|
|
9635
10135
|
Before you write the recipe, ENUMERATE the uniqueness rules for every entity you seed:
|
|
9636
10136
|
read the schema definitions and migrations, and query the live database you are already
|
|
@@ -9694,6 +10194,11 @@ Once every entity passes independently, run the FULL recipe as one pass:
|
|
|
9694
10194
|
\u2022 confirm all rows created (DB), then down with the refsToken -> succeeds, rows gone (DB)
|
|
9695
10195
|
\u2022 confirm a WRONG signature is rejected (the SDK does this for you - do not disable it)
|
|
9696
10196
|
\u2022 confirm the up response's auth payload contains real credentials, not a placeholder
|
|
10197
|
+
\u2022 for every field you identified as compared against the current time, confirm the
|
|
10198
|
+
seeded row landed on the INTENDED side of now - run the app's own query or open the
|
|
10199
|
+
screen that partitions by time, and see the row where it is supposed to be. A row
|
|
10200
|
+
that exists in the right table but falls on the wrong side of now is the failure this
|
|
10201
|
+
check exists to catch, and "up returned 200" will not reveal it.
|
|
9697
10202
|
|
|
9698
10203
|
\u2550\u2550\u2550 CHECK THE RECIPE FILE - THE GATE YOU CANNOT SKIP \u2550\u2550\u2550
|
|
9699
10204
|
A recipe that seeds a database perfectly can still be a file Autonoma refuses, because
|
|
@@ -9762,7 +10267,7 @@ Write the completion marker once ALL of these hold:
|
|
|
9762
10267
|
A step you documented as genuinely blocked NEVER justifies withholding the marker; a
|
|
9763
10268
|
checklist item you simply have not finished always does. The marker is how the CLI knows
|
|
9764
10269
|
the session is done and can upload the recipe:
|
|
9765
|
-
${
|
|
10270
|
+
${join28(params.outputDir, COMPLETION_MARKER_FILE)}
|
|
9766
10271
|
Its contents MUST be exactly:
|
|
9767
10272
|
{ "complete": true }
|
|
9768
10273
|
Writing it is not optional: the planner watches for this marker and takes the terminal
|
|
@@ -9774,27 +10279,45 @@ or the commit you left behind and what blocked the push - and then says:
|
|
|
9774
10279
|
Nothing after that message - no further questions, summaries, or work.`;
|
|
9775
10280
|
}
|
|
9776
10281
|
async function writeIntegrationPrompt(params) {
|
|
9777
|
-
const path3 =
|
|
9778
|
-
await
|
|
10282
|
+
const path3 = join28(params.outputDir, INTEGRATION_PROMPT_FILE);
|
|
10283
|
+
await writeFile9(path3, renderIntegrationPrompt(params), "utf-8");
|
|
9779
10284
|
return path3;
|
|
9780
10285
|
}
|
|
9781
|
-
var INTEGRATION_PROMPT_VERSION, INTEGRATION_PROMPT_FILE, DEFAULT_ENDPOINT_PATH
|
|
10286
|
+
var INTEGRATION_PROMPT_VERSION, INTEGRATION_PROMPT_FILE, DEFAULT_ENDPOINT_PATH;
|
|
9782
10287
|
var init_integration_prompt = __esm({
|
|
9783
10288
|
"src/agents/04-recipe-builder/integration-prompt.ts"() {
|
|
9784
10289
|
"use strict";
|
|
9785
10290
|
init_esm_shims();
|
|
9786
10291
|
init_src();
|
|
9787
10292
|
init_completion();
|
|
9788
|
-
INTEGRATION_PROMPT_VERSION =
|
|
10293
|
+
INTEGRATION_PROMPT_VERSION = 11;
|
|
9789
10294
|
INTEGRATION_PROMPT_FILE = "integration-prompt.md";
|
|
9790
10295
|
DEFAULT_ENDPOINT_PATH = "/api/autonoma";
|
|
9791
|
-
|
|
10296
|
+
}
|
|
10297
|
+
});
|
|
10298
|
+
|
|
10299
|
+
// src/agents/04-recipe-builder/launch-outcome.ts
|
|
10300
|
+
function describeLaunchOutcome(launch) {
|
|
10301
|
+
const ranTooBriefly = launch.durationMs < FAILED_TO_START_MS;
|
|
10302
|
+
const exitedBadly = launch.exitCode != null && launch.exitCode !== 0;
|
|
10303
|
+
if (!ranTooBriefly || !exitedBadly) return { kind: "ran" };
|
|
10304
|
+
return {
|
|
10305
|
+
kind: "failed-to-start",
|
|
10306
|
+
summary: `${launch.agentLabel} exited immediately (code ${launch.exitCode}, after ${Math.round(launch.durationMs / 1e3)}s) without running the integration. That is the agent itself failing to start, not the integration failing - check that it runs on its own in this terminal.`
|
|
10307
|
+
};
|
|
10308
|
+
}
|
|
10309
|
+
var FAILED_TO_START_MS;
|
|
10310
|
+
var init_launch_outcome = __esm({
|
|
10311
|
+
"src/agents/04-recipe-builder/launch-outcome.ts"() {
|
|
10312
|
+
"use strict";
|
|
10313
|
+
init_esm_shims();
|
|
10314
|
+
FAILED_TO_START_MS = 9e4;
|
|
9792
10315
|
}
|
|
9793
10316
|
});
|
|
9794
10317
|
|
|
9795
10318
|
// src/agents/04-recipe-builder/state.ts
|
|
9796
|
-
import { readFile as
|
|
9797
|
-
import { join as
|
|
10319
|
+
import { readFile as readFile18, writeFile as writeFile10 } from "fs/promises";
|
|
10320
|
+
import { join as join29 } from "path";
|
|
9798
10321
|
function initialRecipeState() {
|
|
9799
10322
|
return {
|
|
9800
10323
|
phase: "tech-detect",
|
|
@@ -9804,7 +10327,7 @@ function initialRecipeState() {
|
|
|
9804
10327
|
}
|
|
9805
10328
|
async function loadRecipeState(outputDir) {
|
|
9806
10329
|
try {
|
|
9807
|
-
const raw = await
|
|
10330
|
+
const raw = await readFile18(join29(outputDir, STATE_FILE2), "utf-8");
|
|
9808
10331
|
const parsed = JSON.parse(raw);
|
|
9809
10332
|
return parsed;
|
|
9810
10333
|
} catch {
|
|
@@ -9812,7 +10335,7 @@ async function loadRecipeState(outputDir) {
|
|
|
9812
10335
|
}
|
|
9813
10336
|
}
|
|
9814
10337
|
async function saveRecipeState(outputDir, state) {
|
|
9815
|
-
await
|
|
10338
|
+
await writeFile10(join29(outputDir, STATE_FILE2), JSON.stringify(state, null, 2), "utf-8");
|
|
9816
10339
|
}
|
|
9817
10340
|
var STATE_FILE2;
|
|
9818
10341
|
var init_state2 = __esm({
|
|
@@ -9825,12 +10348,12 @@ var init_state2 = __esm({
|
|
|
9825
10348
|
|
|
9826
10349
|
// src/agents/04-recipe-builder/phases/handoff.ts
|
|
9827
10350
|
import { rm as rm2 } from "fs/promises";
|
|
9828
|
-
import { join as
|
|
10351
|
+
import { join as join30 } from "path";
|
|
9829
10352
|
function launchMessage(promptFile) {
|
|
9830
10353
|
return `Read the file ${promptFile} and follow its instructions exactly to integrate Autonoma into this application. It is your complete spec. Do not stop until every item in it is done and you have written the completion marker it describes.`;
|
|
9831
10354
|
}
|
|
9832
10355
|
async function runHandoffPhase(state, deps, outputDir) {
|
|
9833
|
-
const recipePath = state.recipePath ??
|
|
10356
|
+
const recipePath = state.recipePath ?? join30(outputDir, RECIPE_FILE);
|
|
9834
10357
|
state.recipePath = recipePath;
|
|
9835
10358
|
const launcher = await selectLauncher(deps.launchers, state.agentId ?? deps.presetAgentId, deps.interactive);
|
|
9836
10359
|
if (launcher == null) {
|
|
@@ -9839,11 +10362,15 @@ async function runHandoffPhase(state, deps, outputDir) {
|
|
|
9839
10362
|
}
|
|
9840
10363
|
return manualFallback(outputDir, recipePath, deps.cliCommand, NO_AGENT_FOUND);
|
|
9841
10364
|
}
|
|
9842
|
-
const permissionMode =
|
|
10365
|
+
const permissionMode = await resolvePermissionMode({
|
|
10366
|
+
preset: deps.presetPermissionMode,
|
|
10367
|
+
remembered: state.permissionMode,
|
|
10368
|
+
interactive: deps.interactive
|
|
10369
|
+
});
|
|
9843
10370
|
state.agentId = launcher.id;
|
|
9844
10371
|
state.permissionMode = permissionMode;
|
|
9845
10372
|
await saveRecipeState(outputDir, state);
|
|
9846
|
-
await launchAgent(launcher, permissionMode, {
|
|
10373
|
+
state.lastLaunch = await launchAgent(launcher, permissionMode, {
|
|
9847
10374
|
outputDir,
|
|
9848
10375
|
recipePath,
|
|
9849
10376
|
cliCommand: deps.cliCommand,
|
|
@@ -9856,7 +10383,7 @@ async function runHandoffPhase(state, deps, outputDir) {
|
|
|
9856
10383
|
return { kind: "advance" };
|
|
9857
10384
|
}
|
|
9858
10385
|
async function runCompletionPhase(state, deps, outputDir) {
|
|
9859
|
-
const recipePath = state.recipePath ??
|
|
10386
|
+
const recipePath = state.recipePath ?? join30(outputDir, RECIPE_FILE);
|
|
9860
10387
|
while (true) {
|
|
9861
10388
|
const [complete, read] = await Promise.all([readCompletion(outputDir), loadRecipe(outputDir)]);
|
|
9862
10389
|
const uploadProblems = read.status === "ok" ? findRecipeUploadProblems(read.recipe) : [];
|
|
@@ -9872,30 +10399,59 @@ async function runCompletionPhase(state, deps, outputDir) {
|
|
|
9872
10399
|
return await manualFallback(outputDir, recipePath, deps.cliCommand, NO_AGENT_FOUND);
|
|
9873
10400
|
}
|
|
9874
10401
|
const failure = describeIncompleteRecipe(read, uploadProblems, recipePath, deps.cliCommand);
|
|
9875
|
-
|
|
9876
|
-
|
|
9877
|
-
|
|
9878
|
-
|
|
9879
|
-
|
|
9880
|
-
|
|
9881
|
-
|
|
9882
|
-
|
|
9883
|
-
|
|
9884
|
-
|
|
9885
|
-
|
|
9886
|
-
|
|
9887
|
-
|
|
9888
|
-
|
|
9889
|
-
|
|
9890
|
-
|
|
9891
|
-
}
|
|
9892
|
-
return { kind: "handback", summary: handbackSummary(failure, outputDir) };
|
|
10402
|
+
const retry = await planRetry(state, launcher, deps, failure);
|
|
10403
|
+
if (retry.kind === "give-up") return { kind: "handback", summary: handbackSummary(retry.summary, outputDir) };
|
|
10404
|
+
log.warn(retry.announcement);
|
|
10405
|
+
state.agentId = retry.launcher.id;
|
|
10406
|
+
state.priorFailure = retry.priorFailure;
|
|
10407
|
+
await saveRecipeState(outputDir, state);
|
|
10408
|
+
state.lastLaunch = await launchAgent(retry.launcher, state.permissionMode ?? "bypassPermissions", {
|
|
10409
|
+
outputDir,
|
|
10410
|
+
recipePath,
|
|
10411
|
+
cliCommand: deps.cliCommand,
|
|
10412
|
+
interactive: deps.interactive,
|
|
10413
|
+
priorFailure: retry.priorFailure,
|
|
10414
|
+
userGuidance: state.userGuidance
|
|
10415
|
+
});
|
|
10416
|
+
state.launchAttempts = (state.launchAttempts ?? 0) + 1;
|
|
10417
|
+
await saveRecipeState(outputDir, state);
|
|
9893
10418
|
}
|
|
9894
10419
|
}
|
|
10420
|
+
async function planRetry(state, launcher, deps, failure) {
|
|
10421
|
+
const lastLaunch = state.lastLaunch;
|
|
10422
|
+
const outcome = lastLaunch == null ? void 0 : describeLaunchOutcome(lastLaunch);
|
|
10423
|
+
const failedToStart = outcome?.kind === "failed-to-start" ? outcome.summary : void 0;
|
|
10424
|
+
const summary = failedToStart ?? failure;
|
|
10425
|
+
if (launcher == null) return { kind: "give-up", summary };
|
|
10426
|
+
if ((state.launchAttempts ?? 0) >= MAX_LAUNCH_ATTEMPTS) return { kind: "give-up", summary };
|
|
10427
|
+
if (failedToStart != null && lastLaunch != null) {
|
|
10428
|
+
const alternative = await findOtherAvailableAgent(deps.launchers, lastLaunch.agentId);
|
|
10429
|
+
if (alternative == null) return { kind: "give-up", summary: failedToStart };
|
|
10430
|
+
return {
|
|
10431
|
+
kind: "relaunch",
|
|
10432
|
+
launcher: alternative,
|
|
10433
|
+
// No prior failure: nothing was attempted, so telling a fresh agent that a
|
|
10434
|
+
// prior session "did not complete" would send it hunting for work that was
|
|
10435
|
+
// never started.
|
|
10436
|
+
announcement: `${failedToStart} Trying ${alternative.label} instead...`
|
|
10437
|
+
};
|
|
10438
|
+
}
|
|
10439
|
+
return {
|
|
10440
|
+
kind: "relaunch",
|
|
10441
|
+
launcher,
|
|
10442
|
+
priorFailure: failure,
|
|
10443
|
+
announcement: "The integration isn't complete yet. Launching the agent to finish it..."
|
|
10444
|
+
};
|
|
10445
|
+
}
|
|
10446
|
+
async function findOtherAvailableAgent(launchers, failedAgentId) {
|
|
10447
|
+
const others = launchers.filter((l) => l.id !== failedAgentId);
|
|
10448
|
+
const availability = await Promise.all(others.map((l) => l.isAvailable()));
|
|
10449
|
+
return others.find((_, index) => availability[index]);
|
|
10450
|
+
}
|
|
9895
10451
|
function handbackSummary(failure, outputDir) {
|
|
9896
10452
|
return `${failure}
|
|
9897
10453
|
|
|
9898
|
-
Finish the integration (see the prompt at ${
|
|
10454
|
+
Finish the integration (see the prompt at ${join30(outputDir, INTEGRATION_PROMPT_FILE)}), then re-run with --resume.`;
|
|
9899
10455
|
}
|
|
9900
10456
|
async function launchAgent(launcher, permissionMode, target) {
|
|
9901
10457
|
const promptFile = await writeIntegrationPrompt({
|
|
@@ -9905,7 +10461,7 @@ async function launchAgent(launcher, permissionMode, target) {
|
|
|
9905
10461
|
priorFailure: target.priorFailure,
|
|
9906
10462
|
userGuidance: target.userGuidance
|
|
9907
10463
|
});
|
|
9908
|
-
await rm2(
|
|
10464
|
+
await rm2(join30(target.outputDir, COMPLETION_MARKER_FILE), { force: true }).catch((err) => {
|
|
9909
10465
|
debugLog("Could not clear a stale completion marker", { err });
|
|
9910
10466
|
});
|
|
9911
10467
|
log.info(`Launching ${launcher.label}. It will implement the integration - watch and steer it as it works.`);
|
|
@@ -9922,6 +10478,7 @@ async function launchAgent(launcher, permissionMode, target) {
|
|
|
9922
10478
|
});
|
|
9923
10479
|
}
|
|
9924
10480
|
suspend();
|
|
10481
|
+
const startedAt = Date.now();
|
|
9925
10482
|
try {
|
|
9926
10483
|
const exitCode = await launcher.launch({
|
|
9927
10484
|
message: launchMessage(promptFile),
|
|
@@ -9932,7 +10489,22 @@ async function launchAgent(launcher, permissionMode, target) {
|
|
|
9932
10489
|
// own and needs no watcher.
|
|
9933
10490
|
watch: target.interactive ? (proc) => watchForCompletion(target.outputDir, proc) : void 0
|
|
9934
10491
|
});
|
|
9935
|
-
|
|
10492
|
+
const launch = {
|
|
10493
|
+
agentLabel: launcher.label,
|
|
10494
|
+
agentId: launcher.id,
|
|
10495
|
+
exitCode,
|
|
10496
|
+
durationMs: Date.now() - startedAt
|
|
10497
|
+
};
|
|
10498
|
+
log.info(
|
|
10499
|
+
`${launcher.label} exited (code ${exitCode ?? "unknown"}) after ${Math.round(launch.durationMs / 1e3)}s. Back to the planner.`
|
|
10500
|
+
);
|
|
10501
|
+
track("cli_agent_launch_finished", {
|
|
10502
|
+
agent: launcher.id,
|
|
10503
|
+
exit_code: exitCode,
|
|
10504
|
+
duration_ms: launch.durationMs,
|
|
10505
|
+
outcome: describeLaunchOutcome(launch).kind
|
|
10506
|
+
});
|
|
10507
|
+
return launch;
|
|
9936
10508
|
} finally {
|
|
9937
10509
|
resume();
|
|
9938
10510
|
}
|
|
@@ -9979,14 +10551,17 @@ var init_handoff = __esm({
|
|
|
9979
10551
|
"src/agents/04-recipe-builder/phases/handoff.ts"() {
|
|
9980
10552
|
"use strict";
|
|
9981
10553
|
init_esm_shims();
|
|
10554
|
+
init_analytics();
|
|
9982
10555
|
init_coding_agent();
|
|
9983
10556
|
init_debug();
|
|
9984
10557
|
init_interrupt();
|
|
9985
10558
|
init_notify();
|
|
10559
|
+
init_permission_mode();
|
|
9986
10560
|
init_prompts();
|
|
9987
10561
|
init_completion();
|
|
9988
10562
|
init_completion_watch();
|
|
9989
10563
|
init_integration_prompt();
|
|
10564
|
+
init_launch_outcome();
|
|
9990
10565
|
init_recipe();
|
|
9991
10566
|
init_state2();
|
|
9992
10567
|
MAX_LAUNCH_ATTEMPTS = 2;
|
|
@@ -10103,17 +10678,17 @@ var init_recipe_builder = __esm({
|
|
|
10103
10678
|
});
|
|
10104
10679
|
|
|
10105
10680
|
// src/agents/00b-feature-discovery/index.ts
|
|
10106
|
-
import { readFile as
|
|
10107
|
-
import { join as
|
|
10681
|
+
import { readFile as readFile19, writeFile as writeFile11 } from "fs/promises";
|
|
10682
|
+
import { join as join31 } from "path";
|
|
10108
10683
|
import { tool as tool14 } from "ai";
|
|
10109
|
-
import { z as
|
|
10684
|
+
import { z as z52 } from "zod";
|
|
10110
10685
|
async function saveFeatures(outputDir, features) {
|
|
10111
10686
|
const obj = Object.fromEntries(features);
|
|
10112
|
-
await
|
|
10687
|
+
await writeFile11(join31(outputDir, FEATURES_FILE), JSON.stringify(obj, null, 2), "utf-8");
|
|
10113
10688
|
}
|
|
10114
10689
|
async function loadFeatures(outputDir) {
|
|
10115
10690
|
try {
|
|
10116
|
-
const raw = await
|
|
10691
|
+
const raw = await readFile19(join31(outputDir, FEATURES_FILE), "utf-8");
|
|
10117
10692
|
const obj = JSON.parse(raw);
|
|
10118
10693
|
return new Map(Object.entries(obj));
|
|
10119
10694
|
} catch {
|
|
@@ -10147,7 +10722,7 @@ Process every page. Call add_feature for each sub-feature you discover. When don
|
|
|
10147
10722
|
add_feature: tool14({
|
|
10148
10723
|
description: "Add a discovered sub-feature",
|
|
10149
10724
|
inputSchema: Feature.extend({
|
|
10150
|
-
id:
|
|
10725
|
+
id: z52.string().min(1).describe("Unique kebab-case ID (e.g. 'settings-notifications-tab')")
|
|
10151
10726
|
}),
|
|
10152
10727
|
execute: (featureInput) => {
|
|
10153
10728
|
const { id, ...rest } = featureInput;
|
|
@@ -10161,17 +10736,17 @@ Process every page. Call add_feature for each sub-feature you discover. When don
|
|
|
10161
10736
|
}),
|
|
10162
10737
|
view_features: tool14({
|
|
10163
10738
|
description: "View all discovered features so far",
|
|
10164
|
-
inputSchema:
|
|
10739
|
+
inputSchema: z52.object({}),
|
|
10165
10740
|
execute: () => collector.viewFeatures()
|
|
10166
10741
|
}),
|
|
10167
10742
|
view_pages: tool14({
|
|
10168
10743
|
description: "View the pages list to know what to analyze",
|
|
10169
|
-
inputSchema:
|
|
10744
|
+
inputSchema: z52.object({}),
|
|
10170
10745
|
execute: () => pagesDescription
|
|
10171
10746
|
}),
|
|
10172
10747
|
finish: tool14({
|
|
10173
10748
|
description: "Signal that feature discovery is complete",
|
|
10174
|
-
inputSchema:
|
|
10749
|
+
inputSchema: z52.object({ summary: z52.string() }),
|
|
10175
10750
|
execute: async (finishInput) => {
|
|
10176
10751
|
result = {
|
|
10177
10752
|
success: true,
|
|
@@ -10202,13 +10777,13 @@ var init_b_feature_discovery = __esm({
|
|
|
10202
10777
|
init_model();
|
|
10203
10778
|
init_tools();
|
|
10204
10779
|
FEATURES_FILE = "features.json";
|
|
10205
|
-
Feature =
|
|
10206
|
-
name:
|
|
10207
|
-
type:
|
|
10208
|
-
parentPagePath:
|
|
10209
|
-
sourceFiles:
|
|
10210
|
-
interactiveElements:
|
|
10211
|
-
description:
|
|
10780
|
+
Feature = z52.object({
|
|
10781
|
+
name: z52.string().min(1).describe("Human-readable name (e.g. 'Settings > Notifications Tab', 'Create Project Modal')"),
|
|
10782
|
+
type: z52.enum(["tab", "modal", "form", "table", "wizard", "nested-route", "complex-component"]),
|
|
10783
|
+
parentPagePath: z52.string().min(1).describe("The page path this feature belongs to (from the pages list)"),
|
|
10784
|
+
sourceFiles: z52.array(z52.string()).min(1).describe("Relative paths to the source files for this sub-feature"),
|
|
10785
|
+
interactiveElements: z52.number().int().min(0).describe("Count of interactive elements found (buttons, inputs, toggles, etc.)"),
|
|
10786
|
+
description: z52.string().min(10).describe("What this sub-feature does")
|
|
10212
10787
|
});
|
|
10213
10788
|
FeatureCollector = class {
|
|
10214
10789
|
features = /* @__PURE__ */ new Map();
|
|
@@ -10291,21 +10866,21 @@ Use kebab-case IDs that indicate the parent page and feature type:
|
|
|
10291
10866
|
});
|
|
10292
10867
|
|
|
10293
10868
|
// src/agents/05-test-generator/graph.ts
|
|
10294
|
-
import { readFile as
|
|
10295
|
-
import { join as
|
|
10869
|
+
import { readFile as readFile20, writeFile as writeFile12 } from "fs/promises";
|
|
10870
|
+
import { join as join32 } from "path";
|
|
10296
10871
|
function estimateExpectedTests(written, processed, totalNodes) {
|
|
10297
10872
|
if (totalNodes <= 0) return written;
|
|
10298
10873
|
const rate = written > 0 && processed >= LIVE_RATE_MIN_PROCESSED ? written / processed : TESTS_PER_NODE_PRIOR;
|
|
10299
10874
|
return Math.max(written, Math.round(rate * totalNodes));
|
|
10300
10875
|
}
|
|
10301
10876
|
async function saveBfsState(outputDir, state) {
|
|
10302
|
-
const path3 =
|
|
10303
|
-
await
|
|
10877
|
+
const path3 = join32(outputDir, state.stateFile);
|
|
10878
|
+
await writeFile12(path3, JSON.stringify(state.serialize(), null, 2), "utf-8");
|
|
10304
10879
|
}
|
|
10305
10880
|
async function loadBfsState(outputDir) {
|
|
10306
|
-
const path3 =
|
|
10881
|
+
const path3 = join32(outputDir, BFS_STATE_FILE);
|
|
10307
10882
|
try {
|
|
10308
|
-
const raw = await
|
|
10883
|
+
const raw = await readFile20(path3, "utf-8");
|
|
10309
10884
|
return CoverageState.deserialize(JSON.parse(raw));
|
|
10310
10885
|
} catch {
|
|
10311
10886
|
return void 0;
|
|
@@ -10905,13 +11480,13 @@ When you finish, all dimensions are reported so the user knows what's covered an
|
|
|
10905
11480
|
});
|
|
10906
11481
|
|
|
10907
11482
|
// src/agents/05-test-generator/recipe-context.ts
|
|
10908
|
-
import { readFile as
|
|
10909
|
-
import { join as
|
|
10910
|
-
import { z as
|
|
11483
|
+
import { readFile as readFile21 } from "fs/promises";
|
|
11484
|
+
import { join as join33 } from "path";
|
|
11485
|
+
import { z as z53 } from "zod";
|
|
10911
11486
|
async function loadRecipeContext(outputDir) {
|
|
10912
11487
|
let raw;
|
|
10913
11488
|
try {
|
|
10914
|
-
raw = await
|
|
11489
|
+
raw = await readFile21(join33(outputDir, "recipe.json"), "utf-8");
|
|
10915
11490
|
} catch (err) {
|
|
10916
11491
|
debugLog("recipe.json not present; test generation falls back to scenarios.md alone", { err });
|
|
10917
11492
|
return "";
|
|
@@ -10969,13 +11544,13 @@ var init_recipe_context = __esm({
|
|
|
10969
11544
|
init_debug();
|
|
10970
11545
|
STRUCTURAL_KEYS = /* @__PURE__ */ new Set(["_alias", "_ref"]);
|
|
10971
11546
|
TEMPLATE_TOKEN = /\{\{\s*\w+\s*\}\}/;
|
|
10972
|
-
recipeRowSchema =
|
|
10973
|
-
recipeFileSchema =
|
|
10974
|
-
recipes:
|
|
10975
|
-
|
|
10976
|
-
name:
|
|
10977
|
-
description:
|
|
10978
|
-
create:
|
|
11547
|
+
recipeRowSchema = z53.record(z53.string(), z53.unknown());
|
|
11548
|
+
recipeFileSchema = z53.object({
|
|
11549
|
+
recipes: z53.array(
|
|
11550
|
+
z53.object({
|
|
11551
|
+
name: z53.string(),
|
|
11552
|
+
description: z53.string().optional(),
|
|
11553
|
+
create: z53.record(z53.string(), z53.array(recipeRowSchema)).optional()
|
|
10979
11554
|
})
|
|
10980
11555
|
)
|
|
10981
11556
|
});
|
|
@@ -10983,7 +11558,7 @@ var init_recipe_context = __esm({
|
|
|
10983
11558
|
});
|
|
10984
11559
|
|
|
10985
11560
|
// src/agents/05-test-generator/restore-deleted-test.ts
|
|
10986
|
-
import { access, mkdir as mkdir4, writeFile as
|
|
11561
|
+
import { access, mkdir as mkdir4, writeFile as writeFile13 } from "fs/promises";
|
|
10987
11562
|
import { dirname as dirname4 } from "path";
|
|
10988
11563
|
async function restoreDeletedTest(testPath, originalContent) {
|
|
10989
11564
|
try {
|
|
@@ -10994,7 +11569,7 @@ async function restoreDeletedTest(testPath, originalContent) {
|
|
|
10994
11569
|
}
|
|
10995
11570
|
try {
|
|
10996
11571
|
await mkdir4(dirname4(testPath), { recursive: true });
|
|
10997
|
-
await
|
|
11572
|
+
await writeFile13(testPath, originalContent, "utf-8");
|
|
10998
11573
|
return true;
|
|
10999
11574
|
} catch (err) {
|
|
11000
11575
|
console.warn(` [fix] Could not restore ${testPath}: ${err instanceof Error ? err.message : String(err)}`);
|
|
@@ -11061,21 +11636,21 @@ var init_pool = __esm({
|
|
|
11061
11636
|
});
|
|
11062
11637
|
|
|
11063
11638
|
// src/agents/05-test-generator/rubrics.ts
|
|
11064
|
-
import { z as
|
|
11639
|
+
import { z as z54 } from "zod";
|
|
11065
11640
|
function reviewResultSchema(shape) {
|
|
11066
|
-
return
|
|
11641
|
+
return z54.object(shape);
|
|
11067
11642
|
}
|
|
11068
11643
|
var dimensionResultSchema, reviewResultRecordSchema, structuralIntentRubric, flowCompletenessRubric, uiTextRubric, dataAccuracyRubric, ALL_RUBRICS;
|
|
11069
11644
|
var init_rubrics = __esm({
|
|
11070
11645
|
"src/agents/05-test-generator/rubrics.ts"() {
|
|
11071
11646
|
"use strict";
|
|
11072
11647
|
init_esm_shims();
|
|
11073
|
-
dimensionResultSchema =
|
|
11074
|
-
pass:
|
|
11075
|
-
evidence:
|
|
11076
|
-
suggestion:
|
|
11648
|
+
dimensionResultSchema = z54.object({
|
|
11649
|
+
pass: z54.boolean(),
|
|
11650
|
+
evidence: z54.string().describe("What you checked and found - cite file paths, line content, or specific strings"),
|
|
11651
|
+
suggestion: z54.string().optional().describe("What the planner agent should fix, if failing")
|
|
11077
11652
|
});
|
|
11078
|
-
reviewResultRecordSchema =
|
|
11653
|
+
reviewResultRecordSchema = z54.record(z54.string(), dimensionResultSchema);
|
|
11079
11654
|
structuralIntentRubric = {
|
|
11080
11655
|
name: "structural-intent",
|
|
11081
11656
|
maxSteps: 8,
|
|
@@ -11251,12 +11826,12 @@ When done reviewing, call finish with your structured evaluation.`
|
|
|
11251
11826
|
});
|
|
11252
11827
|
|
|
11253
11828
|
// src/agents/05-test-generator/review-pass.ts
|
|
11254
|
-
import { basename as
|
|
11829
|
+
import { basename as basename5 } from "path";
|
|
11255
11830
|
import "ai";
|
|
11256
11831
|
import { tool as tool15 } from "ai";
|
|
11257
11832
|
async function runReviewPass(testContent, testPath, rubric, projectRoot, model, scenarioData) {
|
|
11258
11833
|
let result;
|
|
11259
|
-
const agentLabel = `review:${rubric.name}:${
|
|
11834
|
+
const agentLabel = `review:${rubric.name}:${basename5(testPath)}`;
|
|
11260
11835
|
const { onStepFinish } = buildDefaultStepLogger(agentLabel, rubric.maxSteps);
|
|
11261
11836
|
const finishTool = tool15({
|
|
11262
11837
|
description: "Submit your structured review. Every dimension must have evidence from your investigation.",
|
|
@@ -11321,12 +11896,12 @@ var init_review_pass = __esm({
|
|
|
11321
11896
|
});
|
|
11322
11897
|
|
|
11323
11898
|
// src/agents/05-test-generator/review.ts
|
|
11324
|
-
import { readFile as
|
|
11325
|
-
import { join as
|
|
11899
|
+
import { readFile as readFile22 } from "fs/promises";
|
|
11900
|
+
import { join as join34, relative as relative7, basename as basename6 } from "path";
|
|
11326
11901
|
import "ai";
|
|
11327
11902
|
import { glob as glob5 } from "glob";
|
|
11328
11903
|
function markReview(relativePath, status) {
|
|
11329
|
-
getActiveStore()?.setArtifactReview(
|
|
11904
|
+
getActiveStore()?.setArtifactReview(join34(TESTS_DIR, relativePath), status);
|
|
11330
11905
|
}
|
|
11331
11906
|
function mergeRubricResults(reported) {
|
|
11332
11907
|
const merged = {};
|
|
@@ -11360,7 +11935,7 @@ async function reviewOneTest({
|
|
|
11360
11935
|
dataContract
|
|
11361
11936
|
).catch((err) => {
|
|
11362
11937
|
console.warn(
|
|
11363
|
-
` [review] ${rubric.name} error on ${
|
|
11938
|
+
` [review] ${rubric.name} error on ${basename6(test.relativePath)}: ${err instanceof Error ? err.message : String(err)}`
|
|
11364
11939
|
);
|
|
11365
11940
|
return void 0;
|
|
11366
11941
|
});
|
|
@@ -11381,24 +11956,24 @@ async function readDataContract(outputDir) {
|
|
|
11381
11956
|
const recipe = await loadRecipeContext(outputDir);
|
|
11382
11957
|
if (recipe !== "") return recipe;
|
|
11383
11958
|
try {
|
|
11384
|
-
return await
|
|
11959
|
+
return await readFile22(join34(outputDir, "scenarios.md"), "utf-8");
|
|
11385
11960
|
} catch (err) {
|
|
11386
11961
|
debugLog("No data contract available for review", { err });
|
|
11387
11962
|
return void 0;
|
|
11388
11963
|
}
|
|
11389
11964
|
}
|
|
11390
11965
|
async function runConsolidatedReview(outputDir, projectRoot, model, deadline, settled = /* @__PURE__ */ new Set(), precomputed = [], onProgress) {
|
|
11391
|
-
const testsDir =
|
|
11966
|
+
const testsDir = join34(outputDir, TESTS_DIR);
|
|
11392
11967
|
const logger = createStepLogger("review", 5);
|
|
11393
11968
|
const scenarioData = await readDataContract(outputDir);
|
|
11394
|
-
const testFiles = await glob5(
|
|
11969
|
+
const testFiles = await glob5(join34(testsDir, TEST_FILE_GLOB));
|
|
11395
11970
|
const tests = [];
|
|
11396
11971
|
for (const testPath of testFiles) {
|
|
11397
11972
|
if (!isTestFile(testPath)) continue;
|
|
11398
11973
|
if (testPath.includes("/_invalid/")) continue;
|
|
11399
11974
|
const relativePath = relative7(testsDir, testPath);
|
|
11400
11975
|
if (settled.has(relativePath)) continue;
|
|
11401
|
-
const content = await
|
|
11976
|
+
const content = await readFile22(testPath, "utf-8");
|
|
11402
11977
|
const flowMatch = content.match(/^---\n[\s\S]*?flow:\s*["']?([^"'\n]+)["']?\s*\n[\s\S]*?---/m);
|
|
11403
11978
|
tests.push({ path: testPath, relativePath, content, flow: flowMatch?.[1]?.trim() ?? "unknown" });
|
|
11404
11979
|
}
|
|
@@ -11439,7 +12014,7 @@ async function runConsolidatedReview(outputDir, projectRoot, model, deadline, se
|
|
|
11439
12014
|
);
|
|
11440
12015
|
for (const { item, error } of outcome.failed) {
|
|
11441
12016
|
console.warn(
|
|
11442
|
-
` [review] ${item.rubric.name} error on ${
|
|
12017
|
+
` [review] ${item.rubric.name} error on ${basename6(item.test.path)}: ${error instanceof Error ? error.message : String(error)}`
|
|
11443
12018
|
);
|
|
11444
12019
|
}
|
|
11445
12020
|
const record = (relativePath, rubricName, result) => {
|
|
@@ -11593,7 +12168,7 @@ var init_review_pipeline = __esm({
|
|
|
11593
12168
|
});
|
|
11594
12169
|
|
|
11595
12170
|
// src/agents/05-test-generator/validation.ts
|
|
11596
|
-
import matter4 from "gray-matter";
|
|
12171
|
+
import matter4 from "@11ty/gray-matter";
|
|
11597
12172
|
function isValidVerb(verb) {
|
|
11598
12173
|
return VALID_VERBS.has(verb);
|
|
11599
12174
|
}
|
|
@@ -11657,7 +12232,7 @@ var init_validation = __esm({
|
|
|
11657
12232
|
});
|
|
11658
12233
|
|
|
11659
12234
|
// src/agents/05-test-generator/test-spec.ts
|
|
11660
|
-
import { z as
|
|
12235
|
+
import { z as z55 } from "zod";
|
|
11661
12236
|
function hasPlaceholder(text2) {
|
|
11662
12237
|
if (text2 == null) return false;
|
|
11663
12238
|
return PLACEHOLDER_PATTERNS.some(({ pattern }) => pattern.test(text2));
|
|
@@ -11708,12 +12283,12 @@ var init_test_spec = __esm({
|
|
|
11708
12283
|
{ pattern: /(?<!\{)\{[a-z][a-zA-Z]*\}(?!\})/, name: "bare {variable}" },
|
|
11709
12284
|
{ pattern: /\be\.g\./i, name: '"e.g." example' }
|
|
11710
12285
|
];
|
|
11711
|
-
stepSchema =
|
|
11712
|
-
verb:
|
|
11713
|
-
description:
|
|
12286
|
+
stepSchema = z55.object({
|
|
12287
|
+
verb: z55.enum(STEP_VERBS).describe("The action. Only these verbs exist."),
|
|
12288
|
+
description: z55.string().min(1).describe(
|
|
11714
12289
|
`What to do or check, naming the exact visible text. For assert, the thing expected on screen - e.g. 'text "Transfer Successful"'.`
|
|
11715
12290
|
),
|
|
11716
|
-
location:
|
|
12291
|
+
location: z55.string().optional().describe(
|
|
11717
12292
|
'WHERE on screen the target is - "in the modal", "in the toast notification", "on the Sony WH-1000XM5 product card", "in the dashboard header", "as a page heading". REQUIRED for click, type and assert: the same label routinely appears more than once (a header button and the modal button it opens, "Buy" on every card in a list), and acting on the wrong one fails confusingly. Not needed for scroll or refresh, which act on the page.'
|
|
11718
12293
|
)
|
|
11719
12294
|
}).refine((step) => !requiresLocation(step.verb) || (step.location?.trim() ?? "") !== "", {
|
|
@@ -11723,27 +12298,27 @@ var init_test_spec = __esm({
|
|
|
11723
12298
|
message: "steps carry no variables - use the exact value from the test data, not a placeholder or example",
|
|
11724
12299
|
path: ["description"]
|
|
11725
12300
|
});
|
|
11726
|
-
testSpecSchema =
|
|
11727
|
-
title:
|
|
11728
|
-
description:
|
|
11729
|
-
intent:
|
|
12301
|
+
testSpecSchema = z55.object({
|
|
12302
|
+
title: z55.string().min(1).describe("Short, descriptive test name."),
|
|
12303
|
+
description: z55.string().min(1).describe("One sentence explaining what the test verifies."),
|
|
12304
|
+
intent: z55.string().min(30).describe(
|
|
11730
12305
|
"A specific, falsifiable claim derived from the node's mission: what the user does, what the feature produces, why it matters. Not the steps, not 'the page displays correctly'."
|
|
11731
12306
|
),
|
|
11732
|
-
criticality:
|
|
11733
|
-
scenario:
|
|
11734
|
-
flow:
|
|
11735
|
-
verification:
|
|
12307
|
+
criticality: z55.enum(CRITICALITY_LEVELS),
|
|
12308
|
+
scenario: z55.string().min(1).describe('Which scenario this test uses (usually "standard").'),
|
|
12309
|
+
flow: z55.string().min(1).describe("Which feature/flow this belongs to (must match a flow from AUTONOMA.md)."),
|
|
12310
|
+
verification: z55.string().min(20).describe(
|
|
11736
12311
|
"WHERE to navigate and WHAT to assert to prove the mutation worked. Must name the source of truth - a toast, a confirmation dialog or an inline success indicator is an acknowledgment, not proof."
|
|
11737
12312
|
),
|
|
11738
|
-
setup:
|
|
12313
|
+
setup: z55.string().min(1).describe(
|
|
11739
12314
|
"Which page the user starts on and how they got there. Never authentication - the user is already signed in."
|
|
11740
12315
|
),
|
|
11741
|
-
steps:
|
|
11742
|
-
verificationSteps:
|
|
12316
|
+
steps: z55.array(stepSchema).min(1).describe("The action sequence, in order."),
|
|
12317
|
+
verificationSteps: z55.array(stepSchema).describe(
|
|
11743
12318
|
"Steps that navigate to the source of truth and assert the mutation landed. Implements the `verification` field above."
|
|
11744
12319
|
),
|
|
11745
|
-
expectedResult:
|
|
11746
|
-
notes:
|
|
12320
|
+
expectedResult: z55.string().min(1).describe("What should be true when the test passes."),
|
|
12321
|
+
notes: z55.string().optional().describe(
|
|
11747
12322
|
"Optional free text for anything you could not resolve or want to flag - an ambiguous default state, a element you could not find in the source, an assumption you had to make. Recorded for humans and NEVER written into the test file, so it will not affect execution. Use it instead of guessing silently."
|
|
11748
12323
|
)
|
|
11749
12324
|
}).refine((spec) => spec.steps.filter((step) => isInteractionVerb(step.verb)).length >= MIN_INTERACTIONS, {
|
|
@@ -11754,25 +12329,25 @@ var init_test_spec = __esm({
|
|
|
11754
12329
|
});
|
|
11755
12330
|
|
|
11756
12331
|
// src/agents/05-test-generator/tools.ts
|
|
11757
|
-
import { mkdir as mkdir5, writeFile as
|
|
11758
|
-
import { dirname as dirname5, join as
|
|
12332
|
+
import { mkdir as mkdir5, writeFile as writeFile14 } from "fs/promises";
|
|
12333
|
+
import { dirname as dirname5, join as join35 } from "path";
|
|
11759
12334
|
import { hasToolCall as hasToolCall3, stepCountIs as stepCountIs3, tool as tool16, ToolLoopAgent as ToolLoopAgent3 } from "ai";
|
|
11760
|
-
import { z as
|
|
12335
|
+
import { z as z56 } from "zod";
|
|
11761
12336
|
function buildWriteTestTool(state, outputDir, onWritten) {
|
|
11762
12337
|
return tool16({
|
|
11763
12338
|
description: "Write one test to qa-tests/{folder}/{filename}.md. You supply the test's parts; the file is rendered for you, so do not write markdown or frontmatter yourself.",
|
|
11764
|
-
inputSchema:
|
|
11765
|
-
folder:
|
|
11766
|
-
filename:
|
|
11767
|
-
nodeId:
|
|
12339
|
+
inputSchema: z56.object({
|
|
12340
|
+
folder: z56.string().describe("Subfolder name under qa-tests/"),
|
|
12341
|
+
filename: z56.string().describe(`File name ending in ${TEST_FILE_EXT} (e.g. login-valid-credentials${TEST_FILE_EXT})`),
|
|
12342
|
+
nodeId: z56.string().describe(
|
|
11768
12343
|
"The id next_node returned for this feature, copied verbatim. Not a re-slugged version of it, not a folder path, not the test filename."
|
|
11769
12344
|
),
|
|
11770
12345
|
test: testSpecSchema
|
|
11771
12346
|
}),
|
|
11772
12347
|
execute: async (input) => {
|
|
11773
12348
|
const content = renderTestMarkdown(input.test);
|
|
11774
|
-
const relPath =
|
|
11775
|
-
const absPath =
|
|
12349
|
+
const relPath = join35(TESTS_DIR, input.folder, normalizeTestFilename(input.filename));
|
|
12350
|
+
const absPath = join35(outputDir, relPath);
|
|
11776
12351
|
const nodeId = state.resolveNodeId(input.nodeId, relPath);
|
|
11777
12352
|
if (nodeId == null) {
|
|
11778
12353
|
const stats = state.summary();
|
|
@@ -11791,7 +12366,7 @@ function buildWriteTestTool(state, outputDir, onWritten) {
|
|
|
11791
12366
|
}
|
|
11792
12367
|
try {
|
|
11793
12368
|
await mkdir5(dirname5(absPath), { recursive: true });
|
|
11794
|
-
await
|
|
12369
|
+
await writeFile14(absPath, content, "utf-8");
|
|
11795
12370
|
state.markTested(nodeId, [relPath]);
|
|
11796
12371
|
await saveBfsState(outputDir, state);
|
|
11797
12372
|
const notes = input.test.notes?.trim();
|
|
@@ -11804,7 +12379,7 @@ function buildWriteTestTool(state, outputDir, onWritten) {
|
|
|
11804
12379
|
});
|
|
11805
12380
|
}
|
|
11806
12381
|
onWritten?.({
|
|
11807
|
-
relativePath:
|
|
12382
|
+
relativePath: join35(input.folder, normalizeTestFilename(input.filename)),
|
|
11808
12383
|
content,
|
|
11809
12384
|
flow: input.test.flow
|
|
11810
12385
|
});
|
|
@@ -11842,14 +12417,14 @@ function buildWriteTestTool(state, outputDir, onWritten) {
|
|
|
11842
12417
|
function buildCreateFolderTool(outputDir) {
|
|
11843
12418
|
return tool16({
|
|
11844
12419
|
description: "Create a folder under qa-tests/ for organizing tests.",
|
|
11845
|
-
inputSchema:
|
|
11846
|
-
folder:
|
|
12420
|
+
inputSchema: z56.object({
|
|
12421
|
+
folder: z56.string().describe("Folder name (kebab-case)")
|
|
11847
12422
|
}),
|
|
11848
12423
|
execute: async (input) => {
|
|
11849
|
-
const absPath =
|
|
12424
|
+
const absPath = join35(outputDir, TESTS_DIR, input.folder);
|
|
11850
12425
|
try {
|
|
11851
12426
|
await mkdir5(absPath, { recursive: true });
|
|
11852
|
-
return { path:
|
|
12427
|
+
return { path: join35(TESTS_DIR, input.folder) };
|
|
11853
12428
|
} catch (err) {
|
|
11854
12429
|
const message = err instanceof Error ? err.message : String(err);
|
|
11855
12430
|
return { error: `Failed to create folder: ${message}` };
|
|
@@ -11860,7 +12435,7 @@ function buildCreateFolderTool(outputDir) {
|
|
|
11860
12435
|
function buildNextNodeTool(state, outputDir) {
|
|
11861
12436
|
return tool16({
|
|
11862
12437
|
description: "Get the next node to write tests for. If you called next_node before without writing any tests (via write_test), the previous node is auto-skipped. Returns done:true when all nodes are processed.",
|
|
11863
|
-
inputSchema:
|
|
12438
|
+
inputSchema: z56.object({}),
|
|
11864
12439
|
execute: async () => {
|
|
11865
12440
|
const next = state.nextNode();
|
|
11866
12441
|
await saveBfsState(outputDir, state);
|
|
@@ -11894,7 +12469,7 @@ function buildNextNodeTool(state, outputDir) {
|
|
|
11894
12469
|
function buildGetProgressTool(state) {
|
|
11895
12470
|
return tool16({
|
|
11896
12471
|
description: "Check how many nodes have been tested vs how many remain.",
|
|
11897
|
-
inputSchema:
|
|
12472
|
+
inputSchema: z56.object({}),
|
|
11898
12473
|
execute: async () => {
|
|
11899
12474
|
const stats = state.summary();
|
|
11900
12475
|
const nodes = [...state.nodes.values()].map((n) => ({
|
|
@@ -11910,12 +12485,12 @@ function buildGetProgressTool(state) {
|
|
|
11910
12485
|
function buildSpawnResearcherTool(model, workingDirectory, onHeartbeat) {
|
|
11911
12486
|
return tool16({
|
|
11912
12487
|
description: "Spawn a research subagent to read and analyze source files without polluting your context. Use for complex sub-features where you don't want to read 20 files yourself.",
|
|
11913
|
-
inputSchema:
|
|
11914
|
-
instruction:
|
|
12488
|
+
inputSchema: z56.object({
|
|
12489
|
+
instruction: z56.string().describe("What to research - be specific about files and what to look for")
|
|
11915
12490
|
}),
|
|
11916
12491
|
execute: async (input) => {
|
|
11917
|
-
const resultSchema2 =
|
|
11918
|
-
findings:
|
|
12492
|
+
const resultSchema2 = z56.object({
|
|
12493
|
+
findings: z56.string().describe("Summary of what was found")
|
|
11919
12494
|
});
|
|
11920
12495
|
let result;
|
|
11921
12496
|
const subagent = new ToolLoopAgent3({
|
|
@@ -11968,11 +12543,11 @@ var init_tools2 = __esm({
|
|
|
11968
12543
|
});
|
|
11969
12544
|
|
|
11970
12545
|
// src/agents/05-test-generator/write-index.ts
|
|
11971
|
-
import { readFile as
|
|
11972
|
-
import { join as
|
|
12546
|
+
import { readFile as readFile23, writeFile as writeFile15 } from "fs/promises";
|
|
12547
|
+
import { join as join36 } from "path";
|
|
11973
12548
|
async function scanTest(outputDir, path3) {
|
|
11974
12549
|
try {
|
|
11975
|
-
const content = await
|
|
12550
|
+
const content = await readFile23(join36(outputDir, path3), "utf-8");
|
|
11976
12551
|
return {
|
|
11977
12552
|
criticality: content.match(/criticality:\s*(\w+)/)?.[1] ?? "",
|
|
11978
12553
|
steps: content.match(STEP_VERBS2)?.length ?? 0,
|
|
@@ -12043,7 +12618,7 @@ ${folders.map((f) => `| ${f.name} | ${f.test_count} |`).join("\n")}
|
|
|
12043
12618
|
|
|
12044
12619
|
${[...testsByFolder.entries()].flatMap(([_folder, tests]) => tests.map((t) => `- \`${t}\``)).join("\n")}
|
|
12045
12620
|
${renderSection("Features with no tests", "the run walked these and produced no test for them - re-run the planner to cover them", untested)}${renderSection("Tests lost in review", "the review cycle removed these and nothing could put them back - re-run the planner to regenerate them", lost)}`;
|
|
12046
|
-
await
|
|
12621
|
+
await writeFile15(join36(outputDir, TESTS_DIR, TEST_INDEX_FILE), content, "utf-8");
|
|
12047
12622
|
}
|
|
12048
12623
|
function untestedFeatures(state) {
|
|
12049
12624
|
const withTests = new Set(state.testsWritten.keys());
|
|
@@ -12078,11 +12653,11 @@ __export(test_generator_exports, {
|
|
|
12078
12653
|
runTestGenerator: () => runTestGenerator
|
|
12079
12654
|
});
|
|
12080
12655
|
import { existsSync as existsSync2 } from "fs";
|
|
12081
|
-
import { mkdir as mkdir6, readFile as
|
|
12082
|
-
import { basename as
|
|
12656
|
+
import { mkdir as mkdir6, readFile as readFile24, rmdir, unlink, writeFile as writeFile16 } from "fs/promises";
|
|
12657
|
+
import { basename as basename7, join as join37 } from "path";
|
|
12083
12658
|
import { tool as tool17 } from "ai";
|
|
12084
12659
|
import { glob as glob6 } from "glob";
|
|
12085
|
-
import { z as
|
|
12660
|
+
import { z as z57 } from "zod";
|
|
12086
12661
|
async function preseedQueue(state, projectRoot, pages, features) {
|
|
12087
12662
|
let seeded = 0;
|
|
12088
12663
|
const pageIdByPath = /* @__PURE__ */ new Map();
|
|
@@ -12134,8 +12709,8 @@ async function runTestGenerator(input) {
|
|
|
12134
12709
|
let result;
|
|
12135
12710
|
const finishTool = tool17({
|
|
12136
12711
|
description: "Call when the BFS queue is empty and all routes have been explored.",
|
|
12137
|
-
inputSchema:
|
|
12138
|
-
summary:
|
|
12712
|
+
inputSchema: z57.object({
|
|
12713
|
+
summary: z57.string().describe("Coverage summary")
|
|
12139
12714
|
}),
|
|
12140
12715
|
execute: async (finishInput) => {
|
|
12141
12716
|
const stats = state.summary();
|
|
@@ -12163,7 +12738,7 @@ async function runTestGenerator(input) {
|
|
|
12163
12738
|
});
|
|
12164
12739
|
let kbContext = "";
|
|
12165
12740
|
try {
|
|
12166
|
-
const autonomaMd = await
|
|
12741
|
+
const autonomaMd = await readFile24(join37(input.outputDir, "AUTONOMA.md"), "utf-8");
|
|
12167
12742
|
kbContext += `
|
|
12168
12743
|
## Knowledge Base (AUTONOMA.md)
|
|
12169
12744
|
|
|
@@ -12176,7 +12751,7 @@ ${autonomaMd}
|
|
|
12176
12751
|
kbContext += recipeContext;
|
|
12177
12752
|
} else {
|
|
12178
12753
|
try {
|
|
12179
|
-
const scenariosMd = await
|
|
12754
|
+
const scenariosMd = await readFile24(join37(input.outputDir, "scenarios.md"), "utf-8");
|
|
12180
12755
|
kbContext += `
|
|
12181
12756
|
## Scenarios
|
|
12182
12757
|
|
|
@@ -12474,20 +13049,20 @@ IMPORTANT: Do NOT try to finish early. Process every node via next_node until it
|
|
|
12474
13049
|
}
|
|
12475
13050
|
state.clearReviewProgress();
|
|
12476
13051
|
state.setPhase("checking every test");
|
|
12477
|
-
const allTestFiles = await glob6(
|
|
13052
|
+
const allTestFiles = await glob6(join37(input.outputDir, TESTS_DIR, TEST_FILE_GLOB));
|
|
12478
13053
|
let markedInvalid = 0;
|
|
12479
13054
|
for (const testPath of allTestFiles) {
|
|
12480
13055
|
if (!isTestFile(testPath)) continue;
|
|
12481
13056
|
if (testPath.includes(`/${INVALID_DIR}/`)) continue;
|
|
12482
|
-
const content = await
|
|
13057
|
+
const content = await readFile24(testPath, "utf-8");
|
|
12483
13058
|
const validation = validateTestContent(content);
|
|
12484
13059
|
if (!validation.valid) {
|
|
12485
|
-
const invalidDir =
|
|
13060
|
+
const invalidDir = join37(input.outputDir, TESTS_DIR, INVALID_DIR);
|
|
12486
13061
|
await mkdir6(invalidDir, { recursive: true });
|
|
12487
|
-
const dest =
|
|
13062
|
+
const dest = join37(invalidDir, basename7(testPath));
|
|
12488
13063
|
const annotated = `<!-- VALIDATION ERRORS: ${validation.errors.join("; ")} -->
|
|
12489
13064
|
${content}`;
|
|
12490
|
-
await
|
|
13065
|
+
await writeFile16(dest, annotated, "utf-8");
|
|
12491
13066
|
await unlink(testPath);
|
|
12492
13067
|
markedInvalid++;
|
|
12493
13068
|
}
|
|
@@ -12495,7 +13070,7 @@ ${content}`;
|
|
|
12495
13070
|
if (markedInvalid > 0) {
|
|
12496
13071
|
console.log(` ${markedInvalid} tests still invalid after review cycles - moved to _invalid/`);
|
|
12497
13072
|
}
|
|
12498
|
-
const dirs = await glob6(
|
|
13073
|
+
const dirs = await glob6(join37(input.outputDir, TESTS_DIR, "**/"), {
|
|
12499
13074
|
dot: false
|
|
12500
13075
|
});
|
|
12501
13076
|
for (const dir of dirs.sort((a, b) => b.length - a.length)) {
|
|
@@ -12548,21 +13123,21 @@ async function generateJourneyTests(outputDir, model, projectRoot) {
|
|
|
12548
13123
|
let autonomaMd = "";
|
|
12549
13124
|
let scenariosMd = "";
|
|
12550
13125
|
try {
|
|
12551
|
-
autonomaMd = await
|
|
13126
|
+
autonomaMd = await readFile24(join37(outputDir, "AUTONOMA.md"), "utf-8");
|
|
12552
13127
|
} catch (err) {
|
|
12553
13128
|
debugLog("AUTONOMA.md not present for journey generation", { err });
|
|
12554
13129
|
}
|
|
12555
13130
|
try {
|
|
12556
|
-
scenariosMd = await
|
|
13131
|
+
scenariosMd = await readFile24(join37(outputDir, "scenarios.md"), "utf-8");
|
|
12557
13132
|
} catch (err) {
|
|
12558
13133
|
debugLog("scenarios.md not present for journey generation", { err });
|
|
12559
13134
|
}
|
|
12560
13135
|
if (!autonomaMd) return 0;
|
|
12561
|
-
const existingTests = await glob6(
|
|
13136
|
+
const existingTests = await glob6(join37(outputDir, TESTS_DIR, TEST_FILE_GLOB));
|
|
12562
13137
|
const existingTitles = [];
|
|
12563
13138
|
for (const t of existingTests) {
|
|
12564
13139
|
if (!isTestFile(t)) continue;
|
|
12565
|
-
const content = await
|
|
13140
|
+
const content = await readFile24(t, "utf-8");
|
|
12566
13141
|
const titleMatch = content.match(/title:\s*"([^"]+)"/);
|
|
12567
13142
|
if (titleMatch) existingTitles.push(titleMatch[1]);
|
|
12568
13143
|
}
|
|
@@ -12608,7 +13183,7 @@ Write 5-8 journey tests using the write_test tool with folder "${JOURNEY_NODE_ID
|
|
|
12608
13183
|
let journeyResult;
|
|
12609
13184
|
const journeyFinish = tool17({
|
|
12610
13185
|
description: "Signal journey generation is complete.",
|
|
12611
|
-
inputSchema:
|
|
13186
|
+
inputSchema: z57.object({ summary: z57.string() }),
|
|
12612
13187
|
execute: async (finishInput) => {
|
|
12613
13188
|
journeyResult = {
|
|
12614
13189
|
success: true,
|
|
@@ -14455,14 +15030,14 @@ var init_session_recorder = __esm({
|
|
|
14455
15030
|
});
|
|
14456
15031
|
|
|
14457
15032
|
// src/ui/artifacts/reader.ts
|
|
14458
|
-
import { open, readFile as
|
|
15033
|
+
import { open, readFile as readFile25, stat as stat2 } from "fs/promises";
|
|
14459
15034
|
async function readForLive(absPath) {
|
|
14460
15035
|
try {
|
|
14461
15036
|
const info = await stat2(absPath);
|
|
14462
15037
|
if (!info.isFile()) return void 0;
|
|
14463
15038
|
const kind = kindOf(absPath);
|
|
14464
15039
|
if (info.size <= LARGE_BYTES) {
|
|
14465
|
-
const text2 = await
|
|
15040
|
+
const text2 = await readFile25(absPath, "utf-8");
|
|
14466
15041
|
return { text: text2, kind, truncated: false };
|
|
14467
15042
|
}
|
|
14468
15043
|
const fh = await open(absPath, "r");
|
|
@@ -14756,9 +15331,9 @@ ensureSupportedNode();
|
|
|
14756
15331
|
|
|
14757
15332
|
// src/index.ts
|
|
14758
15333
|
init_submit();
|
|
14759
|
-
import { readFile as
|
|
15334
|
+
import { readFile as readFile26, writeFile as writeFile17 } from "fs/promises";
|
|
14760
15335
|
import { freemem } from "os";
|
|
14761
|
-
import { delimiter, join as
|
|
15336
|
+
import { delimiter, join as join38 } from "path";
|
|
14762
15337
|
|
|
14763
15338
|
// src/agents/04-recipe-builder/sdk-command.ts
|
|
14764
15339
|
init_esm_shims();
|
|
@@ -14947,7 +15522,7 @@ function shortHash(seed) {
|
|
|
14947
15522
|
}
|
|
14948
15523
|
|
|
14949
15524
|
// src/agents/04-recipe-builder/sdk-command.ts
|
|
14950
|
-
import { z as
|
|
15525
|
+
import { z as z34 } from "zod";
|
|
14951
15526
|
|
|
14952
15527
|
// src/core/cli-args.ts
|
|
14953
15528
|
init_esm_shims();
|
|
@@ -15106,16 +15681,16 @@ async function down(config, refsToken) {
|
|
|
15106
15681
|
init_recipe();
|
|
15107
15682
|
var DEFAULT_REQUEST_TIMEOUT_MS = 12e4;
|
|
15108
15683
|
var MIN_REPEAT_INSTANCES = 2;
|
|
15109
|
-
var createSchema =
|
|
15110
|
-
var recipeSliceSchema =
|
|
15111
|
-
var envelopeSchema =
|
|
15112
|
-
var flagsSchema =
|
|
15113
|
-
url:
|
|
15114
|
-
recipe:
|
|
15115
|
-
"refs-token":
|
|
15116
|
-
"test-run-id":
|
|
15117
|
-
timeout:
|
|
15118
|
-
repeat:
|
|
15684
|
+
var createSchema = z34.record(z34.string(), z34.array(z34.unknown()));
|
|
15685
|
+
var recipeSliceSchema = z34.object({ create: createSchema, variables: ScenarioRecipeVariablesSchema.optional() });
|
|
15686
|
+
var envelopeSchema = z34.object({ recipes: z34.array(recipeSliceSchema).min(1) });
|
|
15687
|
+
var flagsSchema = z34.object({
|
|
15688
|
+
url: z34.string().min(1).optional(),
|
|
15689
|
+
recipe: z34.string().min(1).optional(),
|
|
15690
|
+
"refs-token": z34.string().min(1).optional(),
|
|
15691
|
+
"test-run-id": z34.string().min(1).optional(),
|
|
15692
|
+
timeout: z34.coerce.number().int().positive().optional(),
|
|
15693
|
+
repeat: z34.coerce.number().int().min(MIN_REPEAT_INSTANCES).optional()
|
|
15119
15694
|
});
|
|
15120
15695
|
var SDK_FLAGS = new Set(Object.keys(flagsSchema.shape));
|
|
15121
15696
|
async function runSdkCommand(argv, io) {
|
|
@@ -15680,6 +16255,24 @@ init_front_door();
|
|
|
15680
16255
|
// src/core/help.ts
|
|
15681
16256
|
init_esm_shims();
|
|
15682
16257
|
init_colors();
|
|
16258
|
+
|
|
16259
|
+
// src/core/self-invocation.ts
|
|
16260
|
+
init_esm_shims();
|
|
16261
|
+
import { basename as basename4, sep } from "path";
|
|
16262
|
+
var NPX_INVOCATION = "npx @autonoma-ai/planner@latest";
|
|
16263
|
+
var BIN_NAME = "autonoma-planner";
|
|
16264
|
+
function selfInvocation(argv = process.argv, execPath = process.execPath) {
|
|
16265
|
+
const scriptPath = argv[1];
|
|
16266
|
+
if (scriptPath == null || scriptPath === "") return NPX_INVOCATION;
|
|
16267
|
+
if (basename4(scriptPath) === BIN_NAME) return BIN_NAME;
|
|
16268
|
+
if (isNpxCachePath(scriptPath)) return NPX_INVOCATION;
|
|
16269
|
+
return `${execPath} ${scriptPath}`;
|
|
16270
|
+
}
|
|
16271
|
+
function isNpxCachePath(scriptPath) {
|
|
16272
|
+
return scriptPath.split(sep).includes("_npx");
|
|
16273
|
+
}
|
|
16274
|
+
|
|
16275
|
+
// src/core/help.ts
|
|
15683
16276
|
var DOCS_URL = "https://docs.autonoma.app/test-planner/";
|
|
15684
16277
|
var LLMS_URL = "https://docs.autonoma.app/llms.txt";
|
|
15685
16278
|
var LLMS_FULL_URL = "https://docs.autonoma.app/llms-full.txt";
|
|
@@ -15782,18 +16375,27 @@ var ENVIRONMENT = [
|
|
|
15782
16375
|
" up the preview environment and validates the result; leave it unset",
|
|
15783
16376
|
" and the planner runs standalone against any repository.",
|
|
15784
16377
|
" AUTONOMA_API_URL Point at a non-production Autonoma. Defaults to production.",
|
|
15785
|
-
|
|
15786
|
-
"
|
|
16378
|
+
// Both are read as `1`/`true` exactly (core/posthog.ts, core/debug-sink.ts), so
|
|
16379
|
+
// "set to anything" invites DONT_TRACK=0 - which reads as opted IN.
|
|
16380
|
+
" AUTONOMA_DEBUG Set to 1 or true for diagnostics on stderr, plus a full JSONL",
|
|
16381
|
+
" transcript at ~/.autonoma/debug/<run-id>.jsonl.",
|
|
16382
|
+
" AUTONOMA_DEBUG_FILE Write that transcript to this path instead, without the stderr",
|
|
16383
|
+
" noise. Independent of DONT_TRACK - it never leaves your machine.",
|
|
16384
|
+
" DONT_TRACK Set to 1 or true to turn off analytics, logs and session replay."
|
|
15787
16385
|
];
|
|
15788
16386
|
function renderHelp() {
|
|
16387
|
+
const self = selfInvocation();
|
|
15789
16388
|
return [
|
|
15790
|
-
`${BOLD}autonoma-planner${RESET} - generate an end-to-end test suite from your codebase`,
|
|
16389
|
+
`${BOLD}@autonoma-ai/planner${RESET} - generate an end-to-end test suite from your codebase`,
|
|
15791
16390
|
"",
|
|
16391
|
+
// Spelled the way this run was actually reached. Almost everyone arrives via
|
|
16392
|
+
// npx, which installs nothing on PATH, so a synopsis written as a bare
|
|
16393
|
+
// `autonoma-planner` teaches a command the reader does not have.
|
|
15792
16394
|
`${BOLD}USAGE${RESET}`,
|
|
15793
|
-
|
|
15794
|
-
|
|
15795
|
-
|
|
15796
|
-
|
|
16395
|
+
` ${self} [run] [flags] Plan and generate the suite. \`run\` may be omitted.`,
|
|
16396
|
+
` ${self} status Show what a previous run completed.`,
|
|
16397
|
+
` ${self} upload Re-upload an already-generated suite.`,
|
|
16398
|
+
` ${self} help Print this.`,
|
|
15797
16399
|
"",
|
|
15798
16400
|
`${BOLD}WHAT IT DOES${RESET}`,
|
|
15799
16401
|
...WHAT_IT_DOES.map(indent),
|
|
@@ -15858,8 +16460,8 @@ init_prompts();
|
|
|
15858
16460
|
// src/core/git.ts
|
|
15859
16461
|
init_esm_shims();
|
|
15860
16462
|
import { execFile } from "child_process";
|
|
15861
|
-
import { readFile as
|
|
15862
|
-
import { join as
|
|
16463
|
+
import { readFile as readFile6, writeFile as writeFile5 } from "fs/promises";
|
|
16464
|
+
import { join as join14 } from "path";
|
|
15863
16465
|
import { promisify } from "util";
|
|
15864
16466
|
var execFileAsync = promisify(execFile);
|
|
15865
16467
|
var GIT_INFO_FILE = ".git-info.json";
|
|
@@ -15883,11 +16485,11 @@ async function readGitInfo(projectRoot) {
|
|
|
15883
16485
|
};
|
|
15884
16486
|
}
|
|
15885
16487
|
async function saveGitInfo(outputDir, info) {
|
|
15886
|
-
await
|
|
16488
|
+
await writeFile5(join14(outputDir, GIT_INFO_FILE), JSON.stringify(info, null, 2), "utf-8");
|
|
15887
16489
|
}
|
|
15888
16490
|
async function loadGitInfo(outputDir) {
|
|
15889
16491
|
try {
|
|
15890
|
-
const raw = await
|
|
16492
|
+
const raw = await readFile6(join14(outputDir, GIT_INFO_FILE), "utf-8");
|
|
15891
16493
|
const parsed = JSON.parse(raw);
|
|
15892
16494
|
if (typeof parsed === "object" && parsed != null && "sha" in parsed && typeof parsed.sha === "string") {
|
|
15893
16495
|
const branch = "branch" in parsed && typeof parsed.branch === "string" ? parsed.branch : void 0;
|
|
@@ -15909,15 +16511,15 @@ init_state();
|
|
|
15909
16511
|
init_esm_shims();
|
|
15910
16512
|
init_prompts();
|
|
15911
16513
|
init_debug();
|
|
15912
|
-
import { readFile as
|
|
15913
|
-
import { join as
|
|
16514
|
+
import { readFile as readFile8 } from "fs/promises";
|
|
16515
|
+
import { join as join16 } from "path";
|
|
15914
16516
|
init_test_files();
|
|
15915
16517
|
var ARTIFACT_FILES = ["AUTONOMA.md", "scenarios.md", "entity-audit.md"];
|
|
15916
16518
|
async function readArtifacts(outputDir) {
|
|
15917
16519
|
const files = await Promise.all(
|
|
15918
16520
|
ARTIFACT_FILES.map(async (name) => {
|
|
15919
16521
|
try {
|
|
15920
|
-
return { name, content: await
|
|
16522
|
+
return { name, content: await readFile8(join16(outputDir, name), "utf-8") };
|
|
15921
16523
|
} catch (err) {
|
|
15922
16524
|
debugLog(`Artifact ${name} not on disk; skipping upload`, { err });
|
|
15923
16525
|
return void 0;
|
|
@@ -15933,7 +16535,7 @@ async function readTestCases(outputDir) {
|
|
|
15933
16535
|
const segments = testPath.split("/").slice(1);
|
|
15934
16536
|
const name = segments[segments.length - 1];
|
|
15935
16537
|
const folder = segments.slice(0, -1).join("/");
|
|
15936
|
-
const content = await
|
|
16538
|
+
const content = await readFile8(join16(outputDir, testPath), "utf-8");
|
|
15937
16539
|
return { name, content, folder: folder.length > 0 ? folder : void 0 };
|
|
15938
16540
|
})
|
|
15939
16541
|
);
|
|
@@ -15998,11 +16600,11 @@ process.setSourceMapsEnabled(true);
|
|
|
15998
16600
|
var PAGES_FILE = "pages.json";
|
|
15999
16601
|
async function savePages(outputDir, pages) {
|
|
16000
16602
|
const obj = Object.fromEntries(pages);
|
|
16001
|
-
await
|
|
16603
|
+
await writeFile17(join38(outputDir, PAGES_FILE), JSON.stringify(obj, null, 2), "utf-8");
|
|
16002
16604
|
}
|
|
16003
16605
|
async function loadPages(outputDir) {
|
|
16004
16606
|
try {
|
|
16005
|
-
const raw = await
|
|
16607
|
+
const raw = await readFile26(join38(outputDir, PAGES_FILE), "utf-8");
|
|
16006
16608
|
const obj = JSON.parse(raw);
|
|
16007
16609
|
return new Map(Object.entries(obj));
|
|
16008
16610
|
} catch {
|
|
@@ -16371,6 +16973,23 @@ function ensureAutonomaAuth() {
|
|
|
16371
16973
|
);
|
|
16372
16974
|
return false;
|
|
16373
16975
|
}
|
|
16976
|
+
async function runPreviewHandoffStep(frontDoor, config, nonInteractive) {
|
|
16977
|
+
const { runPreviewHandoff: runPreviewHandoff2, describeIncompletePreview: describeIncompletePreview2 } = await Promise.resolve().then(() => (init_front_door(), front_door_exports));
|
|
16978
|
+
try {
|
|
16979
|
+
const result = await runPreviewHandoff2({ plan: frontDoor, config, nonInteractive });
|
|
16980
|
+
track("cli_preview_phase_finished", { outcome: result.kind });
|
|
16981
|
+
const problem = describeIncompletePreview2(result);
|
|
16982
|
+
if (problem != null) log.warn(problem);
|
|
16983
|
+
} catch (err) {
|
|
16984
|
+
const message = err instanceof Error ? err.message : String(err);
|
|
16985
|
+
track("cli_preview_phase_finished", { outcome: "error" });
|
|
16986
|
+
trackError(err, { source: "preview_phase" });
|
|
16987
|
+
log.warn(`Couldn't set up your preview environment: ${message}`);
|
|
16988
|
+
log.info(
|
|
16989
|
+
"The rest of the run continues - finish the preview in the Autonoma app, or run again once it's sorted."
|
|
16990
|
+
);
|
|
16991
|
+
}
|
|
16992
|
+
}
|
|
16374
16993
|
async function finishFrontDoorRun(frontDoor, config, nonInteractive) {
|
|
16375
16994
|
try {
|
|
16376
16995
|
const gitInfo = await readGitInfo(config.projectRoot);
|
|
@@ -16441,7 +17060,8 @@ async function main() {
|
|
|
16441
17060
|
}
|
|
16442
17061
|
console.log(BANNER);
|
|
16443
17062
|
const projectArg = args.value("project");
|
|
16444
|
-
const
|
|
17063
|
+
const self = selfInvocation();
|
|
17064
|
+
const resumeCommand = `${self} --resume` + (projectArg != null ? ` --project ${projectArg}` : "");
|
|
16445
17065
|
let mountedUi;
|
|
16446
17066
|
let stoppedAt;
|
|
16447
17067
|
installInterruptHandler({
|
|
@@ -16528,11 +17148,7 @@ async function main() {
|
|
|
16528
17148
|
});
|
|
16529
17149
|
}
|
|
16530
17150
|
if (frontDoor != null && previewPending) {
|
|
16531
|
-
|
|
16532
|
-
const result = await runPreviewHandoff2({ plan: frontDoor, config, nonInteractive });
|
|
16533
|
-
track("cli_preview_phase_finished", { outcome: result.kind });
|
|
16534
|
-
const problem = describeIncompletePreview2(result);
|
|
16535
|
-
if (problem != null) log.warn(problem);
|
|
17151
|
+
await runPreviewHandoffStep(frontDoor, config, nonInteractive);
|
|
16536
17152
|
}
|
|
16537
17153
|
if (args.has("fresh") && hasProgress) {
|
|
16538
17154
|
await clearOutputDir(config.projectSlug);
|
|
@@ -16591,7 +17207,7 @@ or reveal hidden files (macOS: Cmd+Shift+. ) to see it.`,
|
|
|
16591
17207
|
mountedUi?.unmount();
|
|
16592
17208
|
mountedUi = void 0;
|
|
16593
17209
|
if (state.steps[targetStep] === "failed") {
|
|
16594
|
-
const retryCommand =
|
|
17210
|
+
const retryCommand = `${self} --step ${targetStep}` + (projectArg != null ? ` --project ${projectArg}` : "");
|
|
16595
17211
|
log.warn(`Your progress is saved. To retry this step, run:
|
|
16596
17212
|
${retryCommand}`);
|
|
16597
17213
|
process.exitCode = 1;
|