codeden 0.2.0 → 0.3.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/THIRD-PARTY-NOTICES.txt +26 -0
- package/dist/cli.js +1 -1
- package/dist/drizzle/20261108085346_automation_created_by/migration.sql +1 -0
- package/dist/drizzle/20261108085346_automation_created_by/snapshot.json +3464 -0
- package/dist/drizzle/20261108090252_project_remote_warning/migration.sql +1 -0
- package/dist/drizzle/20261108090252_project_remote_warning/snapshot.json +3474 -0
- package/dist/{main-pXwL7GDQ.js → main-BAX1sPiD.js} +20558 -18745
- package/dist/main-BAX1sPiD.js.map +1 -0
- package/dist/{main-CzmG0d8t.js → main-DgpYK_Si.js} +2 -2
- package/dist/{main-CzmG0d8t.js.map → main-DgpYK_Si.js.map} +1 -1
- package/dist/{runner-CJetH9jW.js → runner-DGomZl80.js} +649 -110
- package/dist/runner-DGomZl80.js.map +1 -0
- package/dist/runner.js +1 -1
- package/dist/web/assets/{core-BeOBW-Wr.js → core-D2h_xdbh.js} +1 -1
- package/dist/web/assets/engine-oniguruma-CG0oBFVI.js +1 -0
- package/dist/web/assets/{highlighted-body-JLMTFACH-D-L40tHI.js → highlighted-body-JLMTFACH-DpId3Lg2.js} +1 -1
- package/dist/web/assets/{highlights-ZCqssNA3.js → highlights-C1SeynWZ.js} +1 -1
- package/dist/web/assets/index-BPuGYbOr.css +1 -0
- package/dist/web/assets/{index-DR-I5EC5.js → index-BXhyz2mO.js} +240 -218
- package/dist/web/assets/langs-NwkcQ-R2.js +1 -0
- package/dist/web/assets/mermaid-WFAXDIZX-jqhY_qSL.js +1 -0
- package/dist/web/assets/{shiki-CWXnlX2q.js → shiki-h6vxFpD2.js} +2 -2
- package/dist/web/assets/{stream-tokenizer-BpK1CZzW.js → stream-tokenizer-D8Euixi9.js} +1 -1
- package/dist/web/assets/{themes-Bm7B3n10.js → themes-Bi1mPEwQ.js} +2 -2
- package/dist/web/index.html +2 -2
- package/package.json +1 -1
- package/dist/main-pXwL7GDQ.js.map +0 -1
- package/dist/runner-CJetH9jW.js.map +0 -1
- package/dist/web/assets/engine-oniguruma-BLpLufIT.js +0 -1
- package/dist/web/assets/index-D8qaPF_E.css +0 -1
- package/dist/web/assets/langs-DKjK0GUN.js +0 -1
- package/dist/web/assets/mermaid-WFAXDIZX-fZTzmfWq.js +0 -1
|
@@ -8,7 +8,7 @@ import { createHash, randomBytes, randomUUID, timingSafeEqual } from "node:crypt
|
|
|
8
8
|
import { execFile, spawn } from "node:child_process";
|
|
9
9
|
import { gunzipSync } from "node:zlib";
|
|
10
10
|
import { createInterface } from "node:readline";
|
|
11
|
-
import { copyFile, mkdir, readFile, rm, rmdir, stat, utimes, writeFile } from "node:fs/promises";
|
|
11
|
+
import { chmod, copyFile, lstat, mkdir, readFile, readdir, realpath, rename, rm, rmdir, stat, unlink, utimes, writeFile } from "node:fs/promises";
|
|
12
12
|
import { STATUS_CODES, ServerResponse, createServer } from "node:http";
|
|
13
13
|
import { Http2ServerRequest, constants as constants$1 } from "node:http2";
|
|
14
14
|
import { Readable } from "node:stream";
|
|
@@ -6004,12 +6004,16 @@ const Role = _enum([
|
|
|
6004
6004
|
"agent"
|
|
6005
6005
|
]);
|
|
6006
6006
|
/**
|
|
6007
|
-
*
|
|
6007
|
+
* Permission levels of an agent session (§11.2), mapped onto each provider's native enforcement.
|
|
6008
|
+
* Shown in the UI as "Permissions"; stored as `autonomy`.
|
|
6009
|
+
* - `read_only`: reads and runs checks, but never edits files, commits or pushes. Needs a
|
|
6010
|
+
* provider with `ProviderCapabilities.readOnly`.
|
|
6008
6011
|
* - `supervised`: every edit and command asks.
|
|
6009
6012
|
* - `auto`: edits and normal commands in the worktree run freely; risky actions ask (default).
|
|
6010
6013
|
* - `full`: no approvals inside the worktree.
|
|
6011
6014
|
*/
|
|
6012
6015
|
const AutonomyLevel = _enum([
|
|
6016
|
+
"read_only",
|
|
6013
6017
|
"supervised",
|
|
6014
6018
|
"auto",
|
|
6015
6019
|
"full"
|
|
@@ -6284,11 +6288,47 @@ const Attachment = discriminatedUnion("type", [object({
|
|
|
6284
6288
|
name: string().optional(),
|
|
6285
6289
|
mediaType: string().optional()
|
|
6286
6290
|
})]);
|
|
6291
|
+
/**
|
|
6292
|
+
* Who sent a message to a session: the user, the orchestrator (to a PM), a PM (to its
|
|
6293
|
+
* sessions), an automation, or CodeDen itself (automatic wake-ups, the factory loop).
|
|
6294
|
+
*/
|
|
6295
|
+
const MessageSender = _enum([
|
|
6296
|
+
"user",
|
|
6297
|
+
"orchestrator",
|
|
6298
|
+
"pm",
|
|
6299
|
+
"automation",
|
|
6300
|
+
"codeden"
|
|
6301
|
+
]);
|
|
6287
6302
|
/** Input that starts or steers a turn: text plus optional image/file attachments. */
|
|
6288
6303
|
const UserInput = object({
|
|
6289
6304
|
text: string(),
|
|
6290
|
-
attachments: array(Attachment).default([])
|
|
6305
|
+
attachments: array(Attachment).default([]),
|
|
6306
|
+
/**
|
|
6307
|
+
* Who wrote the message, when it was not the user: recorded so the transcript can show the
|
|
6308
|
+
* sender without reading the text. Absent on the user's own messages and on every message
|
|
6309
|
+
* stored before senders were recorded.
|
|
6310
|
+
*/
|
|
6311
|
+
from: MessageSender.optional()
|
|
6291
6312
|
});
|
|
6313
|
+
/**
|
|
6314
|
+
* The first line of a message from a PM or the orchestrator, telling the agent who wrote it.
|
|
6315
|
+
* The only definition of these words: the server writes them, the UI recognises them.
|
|
6316
|
+
*/
|
|
6317
|
+
const SENDER_LINES = {
|
|
6318
|
+
pm: "Message from your project manager:",
|
|
6319
|
+
orchestrator: "Message from the orchestrator:"
|
|
6320
|
+
};
|
|
6321
|
+
/**
|
|
6322
|
+
* Input for a message from a PM or the orchestrator: the sender is recorded in `from` and
|
|
6323
|
+
* named for the agent in the text's first line (`SENDER_LINES`).
|
|
6324
|
+
*/
|
|
6325
|
+
function messageFrom(from, text) {
|
|
6326
|
+
return {
|
|
6327
|
+
text: `${SENDER_LINES[from]}\n${text}`,
|
|
6328
|
+
attachments: [],
|
|
6329
|
+
from
|
|
6330
|
+
};
|
|
6331
|
+
}
|
|
6292
6332
|
const itemBase = {
|
|
6293
6333
|
/** Unique within the session. */
|
|
6294
6334
|
id: Id,
|
|
@@ -6813,6 +6853,18 @@ const FactoryLoopState = _enum([
|
|
|
6813
6853
|
"stopped"
|
|
6814
6854
|
]);
|
|
6815
6855
|
// ../../packages/contracts/src/entities/project.ts
|
|
6856
|
+
/**
|
|
6857
|
+
* Something re-detecting a project's folder found that CodeDen does not apply on its own (§9).
|
|
6858
|
+
* `github_remote_missing`: a `github` project's folder no longer has a GitHub `origin` (removed,
|
|
6859
|
+
* pointing at another host, or no git checkout at all). The project keeps its GitHub
|
|
6860
|
+
* coordinates until the user restores the remote or disconnects GitHub.
|
|
6861
|
+
*/
|
|
6862
|
+
const ProjectRemoteWarning = object({
|
|
6863
|
+
code: literal("github_remote_missing"),
|
|
6864
|
+
/** What `origin` points at now, credentials removed; null when there is no `origin`. */
|
|
6865
|
+
remoteUrl: string().nullable(),
|
|
6866
|
+
detectedAt: Timestamp
|
|
6867
|
+
});
|
|
6816
6868
|
const projectBase = {
|
|
6817
6869
|
id: Id,
|
|
6818
6870
|
name: string().min(1),
|
|
@@ -6845,6 +6897,12 @@ const projectBase = {
|
|
|
6845
6897
|
* `projects.reordered`; archived projects keep theirs.
|
|
6846
6898
|
*/
|
|
6847
6899
|
sortOrder: int().nonnegative().default(0),
|
|
6900
|
+
/**
|
|
6901
|
+
* Set when re-detection found the folder's git remote out of step with the project (see
|
|
6902
|
+
* `ProjectRemoteWarning`); absent when all is well. Cleared by the next detection that finds
|
|
6903
|
+
* the GitHub remote again, or by disconnecting GitHub.
|
|
6904
|
+
*/
|
|
6905
|
+
remoteWarning: ProjectRemoteWarning.optional(),
|
|
6848
6906
|
createdAt: Timestamp,
|
|
6849
6907
|
updatedAt: Timestamp,
|
|
6850
6908
|
archivedAt: Timestamp.nullable()
|
|
@@ -6859,6 +6917,9 @@ const GitHubRepo = object({
|
|
|
6859
6917
|
/**
|
|
6860
6918
|
* A project. `github` projects get the full issue/PR/evidence flow; `local` projects
|
|
6861
6919
|
* (no GitHub remote) get sessions, worktrees, checkpoints, diffs and automations only (§9).
|
|
6920
|
+
* The kind is detected when the project is added and again later (on server start, after
|
|
6921
|
+
* agent turns in local projects, on demand): a folder that gains a GitHub `origin` becomes a
|
|
6922
|
+
* `github` project. Losing the remote never downgrades a project on its own.
|
|
6862
6923
|
*/
|
|
6863
6924
|
const Project = discriminatedUnion("kind", [object({
|
|
6864
6925
|
...projectBase,
|
|
@@ -7044,7 +7105,13 @@ const ProviderCapabilities = object({
|
|
|
7044
7105
|
* An autonomy change reaches a running turn from its next tool call. When false (or absent),
|
|
7045
7106
|
* a running turn keeps its autonomy and the change applies from the next turn.
|
|
7046
7107
|
*/
|
|
7047
|
-
liveAutonomy: boolean().optional()
|
|
7108
|
+
liveAutonomy: boolean().optional(),
|
|
7109
|
+
/**
|
|
7110
|
+
* The `read_only` permission level is enforced natively: no file edits, no writing shell
|
|
7111
|
+
* commands, no commits or pushes (§11.2). When false (or absent), sessions on this provider
|
|
7112
|
+
* cannot use `read_only`.
|
|
7113
|
+
*/
|
|
7114
|
+
readOnly: boolean().optional()
|
|
7048
7115
|
});
|
|
7049
7116
|
/** A model a provider offers. */
|
|
7050
7117
|
const ModelInfo = object({
|
|
@@ -7703,7 +7770,11 @@ const RunnerCommandResults = {
|
|
|
7703
7770
|
ok: boolean()
|
|
7704
7771
|
})).optional()
|
|
7705
7772
|
}),
|
|
7706
|
-
"workspace.remove": object({
|
|
7773
|
+
"workspace.remove": object({
|
|
7774
|
+
removed: boolean(),
|
|
7775
|
+
/** A forced removal deleted uncommitted changes (absent from older runners). */
|
|
7776
|
+
discardedChanges: boolean().optional()
|
|
7777
|
+
}),
|
|
7707
7778
|
"session.start": object({ resumeToken: string().nullable() }),
|
|
7708
7779
|
"session.send": Empty,
|
|
7709
7780
|
"session.steer": Empty,
|
|
@@ -7813,13 +7884,7 @@ const QueuedMessage = object({
|
|
|
7813
7884
|
sessionId: Id,
|
|
7814
7885
|
input: UserInput,
|
|
7815
7886
|
/** Who queued it. Events written before senders were recorded were all the user's. */
|
|
7816
|
-
from:
|
|
7817
|
-
"user",
|
|
7818
|
-
"orchestrator",
|
|
7819
|
-
"pm",
|
|
7820
|
-
"automation",
|
|
7821
|
-
"codeden"
|
|
7822
|
-
]).default("user"),
|
|
7887
|
+
from: MessageSender.default("user"),
|
|
7823
7888
|
createdAt: Timestamp
|
|
7824
7889
|
});
|
|
7825
7890
|
object({ messages: array(QueuedMessage) });
|
|
@@ -8171,6 +8236,12 @@ const AutomationGuards = object({
|
|
|
8171
8236
|
/** Run once for missed schedules after downtime. */
|
|
8172
8237
|
catchUpMissed: boolean().default(false)
|
|
8173
8238
|
});
|
|
8239
|
+
/** Who created a database automation (§10). */
|
|
8240
|
+
const AutomationCreator = _enum([
|
|
8241
|
+
"user",
|
|
8242
|
+
"pm",
|
|
8243
|
+
"import"
|
|
8244
|
+
]);
|
|
8174
8245
|
/** An automation: trigger + action + target + guards. Off by default. */
|
|
8175
8246
|
const Automation = object({
|
|
8176
8247
|
id: Id,
|
|
@@ -8196,6 +8267,11 @@ const Automation = object({
|
|
|
8196
8267
|
* `automation:<template id or name slug>`.
|
|
8197
8268
|
*/
|
|
8198
8269
|
workKey: string().min(1).max(200).optional(),
|
|
8270
|
+
/**
|
|
8271
|
+
* Who created a database automation: the user in the UI (`user`, the default when absent),
|
|
8272
|
+
* a PM through `create_automation` (`pm`), or an import of shared files (`import`).
|
|
8273
|
+
*/
|
|
8274
|
+
createdBy: AutomationCreator.optional(),
|
|
8199
8275
|
createdAt: Timestamp,
|
|
8200
8276
|
updatedAt: Timestamp
|
|
8201
8277
|
});
|
|
@@ -9625,7 +9701,7 @@ const Settings = object({
|
|
|
9625
9701
|
/** Stops every project's factory loop and all factory sessions. */
|
|
9626
9702
|
killSwitch: boolean().default(false) }).prefault({}),
|
|
9627
9703
|
/**
|
|
9628
|
-
* Worktree cleanup (§9). Archiving a session
|
|
9704
|
+
* Worktree cleanup (§9). Archiving a session removes its worktree (unless it has uncommitted changes and isn't forced); with `cleanup` on,
|
|
9629
9705
|
* worktrees of sessions inactive for `afterDays` are removed too, unless the session's pull
|
|
9630
9706
|
* request is still open or the worktree has uncommitted changes.
|
|
9631
9707
|
*/
|
|
@@ -9928,20 +10004,22 @@ const CreateProjectRequest = object({
|
|
|
9928
10004
|
defaultProvider: ProviderId.nullable().optional(),
|
|
9929
10005
|
autonomy: AutonomyLevel.nullable().optional()
|
|
9930
10006
|
});
|
|
10007
|
+
/** What CodeDen found in a project folder. */
|
|
10008
|
+
const ProjectDetection = object({
|
|
10009
|
+
isGitRepo: boolean(),
|
|
10010
|
+
/** Top level of the git checkout (may differ from the chosen folder). */
|
|
10011
|
+
gitRoot: string().nullable(),
|
|
10012
|
+
remoteUrl: string().nullable(),
|
|
10013
|
+
github: GitHubRepo.nullable(),
|
|
10014
|
+
defaultBranch: string().nullable(),
|
|
10015
|
+
currentBranch: string().nullable(),
|
|
10016
|
+
packageManager: PackageManager.nullable(),
|
|
10017
|
+
/** Suggested worktree setup commands, e.g. `pnpm install`. */
|
|
10018
|
+
suggestedSetupCommands: array(string())
|
|
10019
|
+
});
|
|
9931
10020
|
object({
|
|
9932
10021
|
project: Project,
|
|
9933
|
-
detected:
|
|
9934
|
-
isGitRepo: boolean(),
|
|
9935
|
-
/** Top level of the git checkout (may differ from the chosen folder). */
|
|
9936
|
-
gitRoot: string().nullable(),
|
|
9937
|
-
remoteUrl: string().nullable(),
|
|
9938
|
-
github: GitHubRepo.nullable(),
|
|
9939
|
-
defaultBranch: string().nullable(),
|
|
9940
|
-
currentBranch: string().nullable(),
|
|
9941
|
-
packageManager: PackageManager.nullable(),
|
|
9942
|
-
/** Suggested worktree setup commands, e.g. `pnpm install`. */
|
|
9943
|
-
suggestedSetupCommands: array(string())
|
|
9944
|
-
})
|
|
10022
|
+
detected: ProjectDetection
|
|
9945
10023
|
});
|
|
9946
10024
|
/** `PATCH /api/projects/:id`. */
|
|
9947
10025
|
const UpdateProjectRequest = object({
|
|
@@ -10540,6 +10618,97 @@ object({
|
|
|
10540
10618
|
/** The session start when accepted (possibly `conflict`); null when declined. */
|
|
10541
10619
|
start: StartSessionResponse.nullable()
|
|
10542
10620
|
});
|
|
10621
|
+
// ../../packages/contracts/src/automations-sharing.ts
|
|
10622
|
+
/**
|
|
10623
|
+
* Sharing automations (§10): duplicate, export as the Markdown + YAML frontmatter file format
|
|
10624
|
+
* (one `.md`, or all of a project's as a `.zip`), and import those files back. Imports are
|
|
10625
|
+
* parsed with the same parser and validation as automation files, never run anything and
|
|
10626
|
+
* always start off.
|
|
10627
|
+
*/
|
|
10628
|
+
/** Size limits for an import, checked by the server before anything is parsed. */
|
|
10629
|
+
const AUTOMATION_IMPORT_LIMITS = {
|
|
10630
|
+
/** Automation files in one import (Markdown files, zip entries or pasted text). */
|
|
10631
|
+
maxFiles: 50,
|
|
10632
|
+
/** One file's text, in UTF-16 code units (about bytes for ASCII). */
|
|
10633
|
+
maxFileChars: 131072,
|
|
10634
|
+
/** All files together, after unzipping. */
|
|
10635
|
+
maxTotalChars: 1048576,
|
|
10636
|
+
/** A zip as uploaded, before base64 (base64 makes it about a third bigger). */
|
|
10637
|
+
maxZipBytes: 1048576,
|
|
10638
|
+
/** Entries a zip may list, `.md` or not (more is refused before inflating anything). */
|
|
10639
|
+
maxZipEntries: 200
|
|
10640
|
+
};
|
|
10641
|
+
/** What to import: Markdown files, a zip of them (base64) and/or pasted Markdown. */
|
|
10642
|
+
const AutomationImportSource = object({
|
|
10643
|
+
files: array(object({
|
|
10644
|
+
/** File name, e.g. `release-notes.md`; its stem becomes the default work key. */
|
|
10645
|
+
name: string().trim().min(1).max(255),
|
|
10646
|
+
content: string().max(AUTOMATION_IMPORT_LIMITS.maxFileChars)
|
|
10647
|
+
})).max(AUTOMATION_IMPORT_LIMITS.maxFiles).optional(),
|
|
10648
|
+
/** A `.zip` of `.md` files, base64-encoded. Other entries are ignored. */
|
|
10649
|
+
zipBase64: string().max(Math.ceil(AUTOMATION_IMPORT_LIMITS.maxZipBytes / 3) * 4 + 4).optional(),
|
|
10650
|
+
/** One automation pasted as text. */
|
|
10651
|
+
text: string().max(AUTOMATION_IMPORT_LIMITS.maxFileChars).optional()
|
|
10652
|
+
}).refine((s) => Boolean(s.files?.length || s.zipBase64 || s.text?.trim()), { message: "Pass files, a zip or pasted text" });
|
|
10653
|
+
/** The definition an import would create (no id, target or switch yet). */
|
|
10654
|
+
const AutomationDraft = object({
|
|
10655
|
+
name: string().min(1),
|
|
10656
|
+
description: string().nullable(),
|
|
10657
|
+
trigger: AutomationTrigger,
|
|
10658
|
+
action: AutomationAction,
|
|
10659
|
+
guards: AutomationGuards,
|
|
10660
|
+
workKey: string().min(1).max(200)
|
|
10661
|
+
});
|
|
10662
|
+
object({ items: array(object({
|
|
10663
|
+
/** File name, unique within the import (duplicates get ` (2)`). */
|
|
10664
|
+
fileName: string(),
|
|
10665
|
+
/** Null when the file is invalid. */
|
|
10666
|
+
automation: AutomationDraft.nullable(),
|
|
10667
|
+
/** Validation error (bad YAML, unknown fields, invalid cron, ...); null when importable. */
|
|
10668
|
+
error: string().nullable(),
|
|
10669
|
+
/** Runs with Full permissions: it never asks before acting. */
|
|
10670
|
+
fullPermissions: boolean()
|
|
10671
|
+
})) });
|
|
10672
|
+
/**
|
|
10673
|
+
* `POST /api/automations/import`: re-parse the same source and create the selected files as
|
|
10674
|
+
* database automations, always off. `projectId` targets one project; omitted, all projects.
|
|
10675
|
+
*/
|
|
10676
|
+
const ImportAutomationsRequest = object({
|
|
10677
|
+
source: AutomationImportSource,
|
|
10678
|
+
projectId: Id.optional(),
|
|
10679
|
+
/** File names (from the preview) to import; omitted, every valid file. */
|
|
10680
|
+
select: array(string()).max(AUTOMATION_IMPORT_LIMITS.maxFiles).optional()
|
|
10681
|
+
});
|
|
10682
|
+
object({
|
|
10683
|
+
/** The new automations, all off. */
|
|
10684
|
+
created: array(AutomationListItem),
|
|
10685
|
+
/** Selected files that could not be imported, with the reason. */
|
|
10686
|
+
failed: array(object({
|
|
10687
|
+
fileName: string(),
|
|
10688
|
+
error: string()
|
|
10689
|
+
}))
|
|
10690
|
+
});
|
|
10691
|
+
object({
|
|
10692
|
+
/** `<slug>.md`. */
|
|
10693
|
+
fileName: string().min(1),
|
|
10694
|
+
markdown: string()
|
|
10695
|
+
});
|
|
10696
|
+
/** `GET /api/automations/export?projectId=`: the query; without a project, every automation. */
|
|
10697
|
+
const ExportAutomationsQuery = object({ projectId: Id.optional() });
|
|
10698
|
+
object({
|
|
10699
|
+
fileName: string().min(1),
|
|
10700
|
+
/** The zip, base64-encoded. */
|
|
10701
|
+
zipBase64: string(),
|
|
10702
|
+
count: int().nonnegative()
|
|
10703
|
+
});
|
|
10704
|
+
/**
|
|
10705
|
+
* `POST /api/automations/:id/duplicate`: an editable copy (also of file-based and built-in
|
|
10706
|
+
* automations), off. `projectId` targets one project; omitted, the original's target.
|
|
10707
|
+
*/
|
|
10708
|
+
const DuplicateAutomationRequest = object({
|
|
10709
|
+
projectId: Id.optional(),
|
|
10710
|
+
name: string().trim().min(1).max(200).optional()
|
|
10711
|
+
});
|
|
10543
10712
|
// ../../packages/contracts/src/browser-api.ts
|
|
10544
10713
|
/**
|
|
10545
10714
|
* Browser panel HTTP and WebSocket shapes (§12.2). Routes live under
|
|
@@ -10911,6 +11080,25 @@ object({
|
|
|
10911
11080
|
message: string().nullable()
|
|
10912
11081
|
})
|
|
10913
11082
|
});
|
|
11083
|
+
object({
|
|
11084
|
+
/** The project after the check (unchanged unless `changed`). */
|
|
11085
|
+
project: Project,
|
|
11086
|
+
outcome: _enum([
|
|
11087
|
+
"unchanged",
|
|
11088
|
+
"git_detected",
|
|
11089
|
+
"github_connected",
|
|
11090
|
+
"github_changed",
|
|
11091
|
+
"github_remote_missing",
|
|
11092
|
+
"folder_missing"
|
|
11093
|
+
]),
|
|
11094
|
+
/** True when the check updated the project (a `project.updated` event was appended). */
|
|
11095
|
+
changed: boolean(),
|
|
11096
|
+
/** One line for the UI, e.g. "Connected to octo/app" or "Still no GitHub remote on origin". */
|
|
11097
|
+
message: string(),
|
|
11098
|
+
/** What was found in the folder; null when the folder is missing. */
|
|
11099
|
+
detected: ProjectDetection.nullable()
|
|
11100
|
+
});
|
|
11101
|
+
object({ project: Project });
|
|
10914
11102
|
// ../../packages/contracts/src/runner/events.ts
|
|
10915
11103
|
/**
|
|
10916
11104
|
* Durable runner -> server events. Journaled by the runner and delivered with a `seq`
|
|
@@ -11439,7 +11627,7 @@ object({
|
|
|
11439
11627
|
* rest of the bundle, so the package version is the single source of truth at runtime;
|
|
11440
11628
|
* everywhere else (workspace, tests) it is `SOURCE_VERSION`.
|
|
11441
11629
|
*/
|
|
11442
|
-
const CODEDEN_VERSION = packageVersion() ?? "0.
|
|
11630
|
+
const CODEDEN_VERSION = packageVersion() ?? "0.3.0";
|
|
11443
11631
|
function packageVersion() {
|
|
11444
11632
|
const value = globalThis.__CODEDEN_PACKAGE_VERSION__;
|
|
11445
11633
|
return typeof value === "string" && value.length > 0 ? value : void 0;
|
|
@@ -15593,9 +15781,11 @@ function explicitModel(model) {
|
|
|
15593
15781
|
}
|
|
15594
15782
|
// ../../packages/providers/src/policy/read-only.ts
|
|
15595
15783
|
/**
|
|
15596
|
-
* Read-only sessions (the orchestrator and PMs,
|
|
15597
|
-
*
|
|
15598
|
-
* read, with no output redirection and no
|
|
15784
|
+
* Read-only sessions (the orchestrator and PMs, and agent sessions with the `read_only`
|
|
15785
|
+
* permission level, §11.2) may inspect a repository but never change it. This allowlist decides
|
|
15786
|
+
* which shell commands they may run: commands that only read, with no output redirection and no
|
|
15787
|
+
* command substitution. Agent sessions may also run the project's checks (tests, lint, type
|
|
15788
|
+
* checks) without fix or update flags. Anything else is denied.
|
|
15599
15789
|
*/
|
|
15600
15790
|
/**
|
|
15601
15791
|
* Split on every `;`, `&&`, `||`, `|` and newline, even inside quotes. Deliberately cruder
|
|
@@ -15705,8 +15895,75 @@ const READ_ONLY_GH = /* @__PURE__ */ new Set([
|
|
|
15705
15895
|
"checks"
|
|
15706
15896
|
]);
|
|
15707
15897
|
const bin = (token) => (token ?? "").replace(/\\/g, "/").split("/").pop()?.toLowerCase().replace(/\.(exe|cmd|bat|ps1)$/, "") ?? "";
|
|
15898
|
+
/** Package managers whose `test`, `lint`, ... scripts count as checks. */
|
|
15899
|
+
const PACKAGE_MANAGERS = /* @__PURE__ */ new Set([
|
|
15900
|
+
"npm",
|
|
15901
|
+
"pnpm",
|
|
15902
|
+
"yarn",
|
|
15903
|
+
"bun"
|
|
15904
|
+
]);
|
|
15905
|
+
/** Script names a package manager may run as a check (`pnpm test`, `npm run lint`). */
|
|
15906
|
+
const CHECK_SCRIPTS = /* @__PURE__ */ new Set([
|
|
15907
|
+
"test",
|
|
15908
|
+
"tests",
|
|
15909
|
+
"lint",
|
|
15910
|
+
"typecheck",
|
|
15911
|
+
"type-check",
|
|
15912
|
+
"check",
|
|
15913
|
+
"types",
|
|
15914
|
+
"vitest",
|
|
15915
|
+
"jest",
|
|
15916
|
+
"biome",
|
|
15917
|
+
"eslint",
|
|
15918
|
+
"tsc"
|
|
15919
|
+
]);
|
|
15920
|
+
/** Programs (null: any arguments) and `<program> <subcommand>` pairs that run checks. */
|
|
15921
|
+
const CHECK_COMMANDS = {
|
|
15922
|
+
tsc: null,
|
|
15923
|
+
vitest: null,
|
|
15924
|
+
jest: null,
|
|
15925
|
+
eslint: null,
|
|
15926
|
+
pytest: null,
|
|
15927
|
+
mypy: null,
|
|
15928
|
+
pyright: null,
|
|
15929
|
+
biome: [
|
|
15930
|
+
"check",
|
|
15931
|
+
"lint",
|
|
15932
|
+
"ci"
|
|
15933
|
+
],
|
|
15934
|
+
ruff: ["check"],
|
|
15935
|
+
cargo: [
|
|
15936
|
+
"test",
|
|
15937
|
+
"check",
|
|
15938
|
+
"clippy"
|
|
15939
|
+
],
|
|
15940
|
+
go: ["test", "vet"],
|
|
15941
|
+
dotnet: ["test"],
|
|
15942
|
+
mvn: ["test", "verify"],
|
|
15943
|
+
gradle: ["test", "check"]
|
|
15944
|
+
};
|
|
15945
|
+
/** Flags that turn a check into a write (fixing, formatting, updating snapshots, watching). */
|
|
15946
|
+
const WRITING_FLAG = /^(--fix|--fix-type=.*|--write|--apply|--apply-unsafe|--unsafe|-u|--update|--update-snapshots?|--updatesnapshot|--watch|-w)(=.*)?$/;
|
|
15947
|
+
/** True when `name args` runs a known check without writing flags. */
|
|
15948
|
+
function isCheck(name, args) {
|
|
15949
|
+
if (args.some((a) => WRITING_FLAG.test(a))) return false;
|
|
15950
|
+
const positional = args.filter((a) => !a.startsWith("-"));
|
|
15951
|
+
if (PACKAGE_MANAGERS.has(name)) {
|
|
15952
|
+
const script = positional[0] === "run" ? positional[1] : positional[0];
|
|
15953
|
+
return script !== void 0 && CHECK_SCRIPTS.has(script);
|
|
15954
|
+
}
|
|
15955
|
+
if (name === "npx" || name === "pnpx" || name === "bunx") {
|
|
15956
|
+
const tool = positional[0];
|
|
15957
|
+
return tool !== void 0 && isCheck(bin(tool), args.slice(args.indexOf(tool) + 1));
|
|
15958
|
+
}
|
|
15959
|
+
if (name === "python" || name === "python3" || name === "py") return args[0] === "-m" && (args[1] === "pytest" || args[1] === "mypy");
|
|
15960
|
+
if (!Object.hasOwn(CHECK_COMMANDS, name)) return false;
|
|
15961
|
+
const subs = CHECK_COMMANDS[name];
|
|
15962
|
+
if (!subs) return true;
|
|
15963
|
+
return positional[0] !== void 0 && subs.includes(positional[0]);
|
|
15964
|
+
}
|
|
15708
15965
|
/** Why a command is refused in a read-only session, or undefined when it may run. */
|
|
15709
|
-
function readOnlyCommandDenial(command) {
|
|
15966
|
+
function readOnlyCommandDenial(command, options = {}) {
|
|
15710
15967
|
if (/\$\(|`|<\(|>\(/.test(command)) return "Command substitution is not allowed in a read-only session";
|
|
15711
15968
|
for (const simple of splitCommands(command)) {
|
|
15712
15969
|
if (/(^|[^0-9&<])>>?(?!\s*(&\d|\/dev\/null|nul\b|\$null))/i.test(simple)) return "Writing files is not allowed in a read-only session";
|
|
@@ -15725,6 +15982,7 @@ function readOnlyCommandDenial(command) {
|
|
|
15725
15982
|
continue;
|
|
15726
15983
|
}
|
|
15727
15984
|
if (name === "find" && args.some((a) => a === "-delete" || a === "-exec" || a === "-execdir")) return "find with -delete or -exec is not allowed in a read-only session";
|
|
15985
|
+
if (options.allowChecks && isCheck(name, args)) continue;
|
|
15728
15986
|
if (!READ_ONLY_BINS.has(name)) return `${name || "This command"} is not on the read-only allowlist`;
|
|
15729
15987
|
}
|
|
15730
15988
|
}
|
|
@@ -15866,7 +16124,7 @@ function tempRootsFrom(candidates, platform, home) {
|
|
|
15866
16124
|
}
|
|
15867
16125
|
return out;
|
|
15868
16126
|
}
|
|
15869
|
-
const realpath = (p) => {
|
|
16127
|
+
const realpath$1 = (p) => {
|
|
15870
16128
|
try {
|
|
15871
16129
|
return realpathSync.native(p);
|
|
15872
16130
|
} catch {
|
|
@@ -15883,7 +16141,7 @@ function osTempRoots(platform = process.platform) {
|
|
|
15883
16141
|
if (platform !== process.platform) return [];
|
|
15884
16142
|
if (cachedTempRoots) return cachedTempRoots;
|
|
15885
16143
|
const env = process.env;
|
|
15886
|
-
cachedTempRoots = tempRootsFrom([os.tmpdir(), ...platform === "win32" ? [env.TEMP, env.TMP] : [env.TMPDIR, "/tmp"]].flatMap((c) => c ? [c, realpath(c)] : []), platform, os.homedir());
|
|
16144
|
+
cachedTempRoots = tempRootsFrom([os.tmpdir(), ...platform === "win32" ? [env.TEMP, env.TMP] : [env.TMPDIR, "/tmp"]].flatMap((c) => c ? [c, realpath$1(c)] : []), platform, os.homedir());
|
|
15887
16145
|
return cachedTempRoots;
|
|
15888
16146
|
}
|
|
15889
16147
|
const SECRET_BASENAMES = /* @__PURE__ */ new Set([
|
|
@@ -16739,6 +16997,8 @@ function approvalTitle(name, input) {
|
|
|
16739
16997
|
// ../../packages/providers/src/claude/permissions.ts
|
|
16740
16998
|
/**
|
|
16741
16999
|
* Native permission mode per autonomy level (§11.2):
|
|
17000
|
+
* - read_only -> `default`; edits and writing commands are denied by the PreToolUse hook
|
|
17001
|
+
* (`ClaudePolicy.denial`), so they never reach a prompt
|
|
16742
17002
|
* - supervised -> `default`, and every edit/command is forced to ask via a PreToolUse hook
|
|
16743
17003
|
* - auto -> `acceptEdits`, plus the risky-action classifier
|
|
16744
17004
|
* - full -> `bypassPermissions`, asking only for writes outside the worktree (the OS
|
|
@@ -16747,7 +17007,7 @@ function approvalTitle(name, input) {
|
|
|
16747
17007
|
*/
|
|
16748
17008
|
function permissionModeFor(autonomy, planMode) {
|
|
16749
17009
|
if (planMode) return "plan";
|
|
16750
|
-
if (autonomy === "supervised") return "default";
|
|
17010
|
+
if (autonomy === "read_only" || autonomy === "supervised") return "default";
|
|
16751
17011
|
if (autonomy === "auto") return "acceptEdits";
|
|
16752
17012
|
return "bypassPermissions";
|
|
16753
17013
|
}
|
|
@@ -16769,16 +17029,23 @@ var ClaudePolicy = class {
|
|
|
16769
17029
|
get level() {
|
|
16770
17030
|
return this.autonomy;
|
|
16771
17031
|
}
|
|
17032
|
+
/**
|
|
17033
|
+
* True while edits and writing commands are refused: a read-only role (orchestrator, PMs),
|
|
17034
|
+
* or the `read_only` permission level, which can change live with `setAutonomy`.
|
|
17035
|
+
*/
|
|
17036
|
+
get isReadOnly() {
|
|
17037
|
+
return this.readOnly || this.autonomy === "read_only";
|
|
17038
|
+
}
|
|
16772
17039
|
/** Switch the level; the next `gate()` uses it. `allow_session` decisions are kept. */
|
|
16773
17040
|
setAutonomy(level) {
|
|
16774
17041
|
this.autonomy = level;
|
|
16775
17042
|
}
|
|
16776
17043
|
/** Why a call is refused outright (read-only sessions), or undefined. */
|
|
16777
17044
|
denial(toolName, input) {
|
|
16778
|
-
if (!this.
|
|
16779
|
-
if (EDIT_TOOLS.has(toolName)) return "This session is read-only: it cannot edit files. Act through CodeDen tools instead.";
|
|
17045
|
+
if (!this.isReadOnly) return void 0;
|
|
17046
|
+
if (EDIT_TOOLS.has(toolName)) return this.readOnly ? "This session is read-only: it cannot edit files. Act through CodeDen tools instead." : "This session has read-only permissions: it cannot edit files, commit or push. Report what you found instead.";
|
|
16780
17047
|
if (COMMAND_TOOLS.has(toolName)) {
|
|
16781
|
-
const reason = readOnlyCommandDenial(str(input, "command") ?? "");
|
|
17048
|
+
const reason = readOnlyCommandDenial(str(input, "command") ?? "", { allowChecks: !this.readOnly });
|
|
16782
17049
|
return reason ? `This session is read-only: ${reason}.` : void 0;
|
|
16783
17050
|
}
|
|
16784
17051
|
}
|
|
@@ -18649,7 +18916,8 @@ const capabilities$2 = {
|
|
|
18649
18916
|
reasoningEffort: true,
|
|
18650
18917
|
mcp: true,
|
|
18651
18918
|
skills: true,
|
|
18652
|
-
liveAutonomy: true
|
|
18919
|
+
liveAutonomy: true,
|
|
18920
|
+
readOnly: true
|
|
18653
18921
|
};
|
|
18654
18922
|
const EFFORTS = [
|
|
18655
18923
|
"low",
|
|
@@ -19302,6 +19570,7 @@ async function handleServerRequest(req, ac) {
|
|
|
19302
19570
|
// ../../packages/providers/src/codex/config.ts
|
|
19303
19571
|
/**
|
|
19304
19572
|
* Native sandbox + approval policy per autonomy level (§11.2):
|
|
19573
|
+
* - read_only -> read-only sandbox + `never`: writes fail instead of asking, no network
|
|
19305
19574
|
* - supervised -> `untrusted`: every command that isn't known-safe and every patch asks
|
|
19306
19575
|
* - auto -> workspace-write sandbox + `on-request`: the sandbox confines writes (to the
|
|
19307
19576
|
* worktree, extra roots and the OS temp directory) and network; escaping it
|
|
@@ -19310,7 +19579,7 @@ async function handleServerRequest(req, ac) {
|
|
|
19310
19579
|
* to the worktree, extra roots and temp
|
|
19311
19580
|
*/
|
|
19312
19581
|
function codexPolicy(autonomy, readOnly = false) {
|
|
19313
|
-
if (readOnly) return {
|
|
19582
|
+
if (readOnly || autonomy === "read_only") return {
|
|
19314
19583
|
approvalPolicy: "never",
|
|
19315
19584
|
sandbox: "read-only",
|
|
19316
19585
|
networkAccess: false
|
|
@@ -19337,7 +19606,7 @@ function codexPolicy(autonomy, readOnly = false) {
|
|
|
19337
19606
|
* never needs an approval (§11.2).
|
|
19338
19607
|
*/
|
|
19339
19608
|
function codexWritableRoots(opts, tempRoots = osTempRoots()) {
|
|
19340
|
-
const temp = opts.readOnly || opts.autonomy === "supervised" ? [] : tempRoots;
|
|
19609
|
+
const temp = opts.readOnly || opts.autonomy === "read_only" || opts.autonomy === "supervised" ? [] : tempRoots;
|
|
19341
19610
|
return [.../* @__PURE__ */ new Set([...opts.additionalDirectories, ...temp])];
|
|
19342
19611
|
}
|
|
19343
19612
|
/**
|
|
@@ -20309,7 +20578,8 @@ const capabilities$1 = {
|
|
|
20309
20578
|
reasoningEffort: true,
|
|
20310
20579
|
mcp: true,
|
|
20311
20580
|
skills: true,
|
|
20312
|
-
liveAutonomy: false
|
|
20581
|
+
liveAutonomy: false,
|
|
20582
|
+
readOnly: true
|
|
20313
20583
|
};
|
|
20314
20584
|
const CONFIGURED_MODEL = "The model configured in ~/.codex/config.toml";
|
|
20315
20585
|
/**
|
|
@@ -20582,7 +20852,8 @@ var FakeSession = class {
|
|
|
20582
20852
|
await this.streamAssistant(turn, threadId, this.replyFor(prompt));
|
|
20583
20853
|
await this.todoList(turn);
|
|
20584
20854
|
if (prompt.includes("[mcp]")) await this.mcpToolCall(turn);
|
|
20585
|
-
|
|
20855
|
+
const readOnly = this.autonomy === "read_only";
|
|
20856
|
+
if ((!readOnly && (this.fake.approval || prompt.includes("[approval]")) && this.autonomy !== "full" ? await this.approval(turn) : true) && !readOnly) await this.commandToolCall(turn);
|
|
20586
20857
|
if (this.fake.subagent || prompt.includes("[subagent]")) await this.subagent(turn);
|
|
20587
20858
|
if (this.fake.followups ?? true) {
|
|
20588
20859
|
await this.step(turn);
|
|
@@ -20975,7 +21246,8 @@ const capabilities = {
|
|
|
20975
21246
|
reasoningEffort: false,
|
|
20976
21247
|
mcp: false,
|
|
20977
21248
|
skills: false,
|
|
20978
|
-
liveAutonomy: true
|
|
21249
|
+
liveAutonomy: true,
|
|
21250
|
+
readOnly: true
|
|
20979
21251
|
};
|
|
20980
21252
|
/**
|
|
20981
21253
|
* Deterministic scripted provider for demos and tests (§7.2). Every turn streams:
|
|
@@ -26296,6 +26568,303 @@ async function listPorts(probe = []) {
|
|
|
26296
26568
|
url: `http://localhost:${port}`
|
|
26297
26569
|
}));
|
|
26298
26570
|
}
|
|
26571
|
+
// ../runner/src/util/paths.ts
|
|
26572
|
+
/** True when `child` is `dir` itself or somewhere inside it. */
|
|
26573
|
+
function isInside(dir, child) {
|
|
26574
|
+
const rel = relative(resolve(dir), resolve(child));
|
|
26575
|
+
return rel === "" || !rel.startsWith("..") && !/^[a-zA-Z]:/.test(rel);
|
|
26576
|
+
}
|
|
26577
|
+
// ../runner/src/workspace/delete-tree.ts
|
|
26578
|
+
/** Error codes Windows returns while another process still holds a file or folder open. */
|
|
26579
|
+
const IN_USE = /* @__PURE__ */ new Set([
|
|
26580
|
+
"EBUSY",
|
|
26581
|
+
"EPERM",
|
|
26582
|
+
"EACCES",
|
|
26583
|
+
"ENOTEMPTY"
|
|
26584
|
+
]);
|
|
26585
|
+
const errorCode = (error) => error && typeof error === "object" && "code" in error ? String(error.code) : void 0;
|
|
26586
|
+
/** Run `op`, retrying briefly (about 1.5s for 5 attempts) while the path is in use. */
|
|
26587
|
+
async function retry(op, attempts = 5) {
|
|
26588
|
+
for (let attempt = 1;; attempt++) try {
|
|
26589
|
+
await op();
|
|
26590
|
+
return;
|
|
26591
|
+
} catch (error) {
|
|
26592
|
+
const code = errorCode(error);
|
|
26593
|
+
if (code === "ENOENT") return;
|
|
26594
|
+
if (!code || !IN_USE.has(code) || attempt >= attempts) throw error;
|
|
26595
|
+
await new Promise((r) => setTimeout(r, 100 * attempt));
|
|
26596
|
+
}
|
|
26597
|
+
}
|
|
26598
|
+
/** Delete a file or link (never what a link points at); clears the read-only flag if needed. */
|
|
26599
|
+
function removeFile(path) {
|
|
26600
|
+
return retry(async () => {
|
|
26601
|
+
try {
|
|
26602
|
+
await unlink(path);
|
|
26603
|
+
} catch (error) {
|
|
26604
|
+
if (errorCode(error) !== "EPERM" && errorCode(error) !== "EACCES") throw error;
|
|
26605
|
+
await chmod(path, 438).catch(() => void 0);
|
|
26606
|
+
await unlink(path);
|
|
26607
|
+
}
|
|
26608
|
+
});
|
|
26609
|
+
}
|
|
26610
|
+
async function kind(path, entry) {
|
|
26611
|
+
if (entry.isSymbolicLink()) return "other";
|
|
26612
|
+
if (entry.isDirectory()) return "dir";
|
|
26613
|
+
if (entry.isFile()) return "other";
|
|
26614
|
+
const stat = await lstat(path).catch(() => void 0);
|
|
26615
|
+
return stat?.isDirectory() && !stat.isSymbolicLink() ? "dir" : "other";
|
|
26616
|
+
}
|
|
26617
|
+
/**
|
|
26618
|
+
* Empty `dir`: files and links are unlinked, real folders are emptied and removed. A symlink
|
|
26619
|
+
* or junction is never descended into (Windows reports junctions as links), so nothing
|
|
26620
|
+
* outside the tree is touched. Every branch is finished before the first failure is thrown.
|
|
26621
|
+
*/
|
|
26622
|
+
async function emptyDir(dir) {
|
|
26623
|
+
let entries;
|
|
26624
|
+
try {
|
|
26625
|
+
entries = await readdir(dir, { withFileTypes: true });
|
|
26626
|
+
} catch (error) {
|
|
26627
|
+
if (errorCode(error) === "ENOENT") return;
|
|
26628
|
+
throw error;
|
|
26629
|
+
}
|
|
26630
|
+
const failed = (await Promise.allSettled(entries.map(async (entry) => {
|
|
26631
|
+
const path = join(dir, entry.name);
|
|
26632
|
+
if (await kind(path, entry) !== "dir") return removeFile(path);
|
|
26633
|
+
await emptyDir(path);
|
|
26634
|
+
await retry(() => rmdir(path));
|
|
26635
|
+
}))).find((r) => r.status === "rejected");
|
|
26636
|
+
if (failed) throw failed.reason;
|
|
26637
|
+
}
|
|
26638
|
+
/** A short, user-facing error for a failed delete or move of the worktree folder `root`. */
|
|
26639
|
+
function deleteFailed(root, error) {
|
|
26640
|
+
const file = error && typeof error === "object" && "path" in error ? String(error.path) : root;
|
|
26641
|
+
const where = relative(root, file);
|
|
26642
|
+
const code = errorCode(error);
|
|
26643
|
+
if (code && IN_USE.has(code)) return new CommandError("worktree_in_use", `${where ? `A file in the worktree is in use (${where})` : "The worktree is in use"}. Close whatever is using it (a terminal, dev server or editor), then try again.`);
|
|
26644
|
+
const reason = error instanceof Error ? error.message : String(error);
|
|
26645
|
+
return new CommandError("remove_failed", `Could not delete ${where || root}: ${reason}`);
|
|
26646
|
+
}
|
|
26647
|
+
/**
|
|
26648
|
+
* Delete a worktree folder without ever following a symlink or junction out of it.
|
|
26649
|
+
*
|
|
26650
|
+
* `git worktree remove` can't be trusted with this on Windows: Git treats junctions as
|
|
26651
|
+
* folders, so it deletes the contents of whatever a live junction points at, and it stops at
|
|
26652
|
+
* a dangling one with "Directory not empty". In a pnpm install the packages are deleted before
|
|
26653
|
+
* the junctions that point at them, so it fails halfway, after removing `.git`. Node's
|
|
26654
|
+
* recursive `rm` is no better here: on Windows it waits for as long as a file stays locked.
|
|
26655
|
+
*
|
|
26656
|
+
* `.git` goes first, so a delete that a locked file interrupts leaves an orphan folder the
|
|
26657
|
+
* next attempt cleans up (never a damaged worktree that looks dirty). Each locked entry is
|
|
26658
|
+
* retried for about 1.5s, then this throws `worktree_in_use`.
|
|
26659
|
+
*/
|
|
26660
|
+
async function deleteTree(root) {
|
|
26661
|
+
try {
|
|
26662
|
+
const stat = await lstat(root).catch(() => void 0);
|
|
26663
|
+
if (!stat) return;
|
|
26664
|
+
if (stat.isSymbolicLink() || !stat.isDirectory()) return await removeFile(root);
|
|
26665
|
+
await removeFile(join(root, ".git"));
|
|
26666
|
+
await emptyDir(root);
|
|
26667
|
+
await retry(() => rmdir(root));
|
|
26668
|
+
} catch (error) {
|
|
26669
|
+
throw deleteFailed(root, error);
|
|
26670
|
+
}
|
|
26671
|
+
}
|
|
26672
|
+
// ../runner/src/workspace/trash.ts
|
|
26673
|
+
/**
|
|
26674
|
+
* True for a session folder CodeDen created and owns: exactly `<home>/wt/<project>/<session>`.
|
|
26675
|
+
* Only these may be deleted once they are no longer git worktrees.
|
|
26676
|
+
*/
|
|
26677
|
+
function isManagedWorktreePath(home, path) {
|
|
26678
|
+
const rel = relative(resolve(home, "wt"), resolve(path));
|
|
26679
|
+
if (!rel || isAbsolute(rel)) return false;
|
|
26680
|
+
const parts = rel.split(/[\\/]+/);
|
|
26681
|
+
return parts.length === 2 && !parts.includes("..") && !parts.includes(".") && parts[0] !== ".trash";
|
|
26682
|
+
}
|
|
26683
|
+
/** Where removed worktrees wait to be deleted. `.trash` can never be a project slug. */
|
|
26684
|
+
function trashDir(home) {
|
|
26685
|
+
return join(resolve(home), "wt", ".trash");
|
|
26686
|
+
}
|
|
26687
|
+
/**
|
|
26688
|
+
* Move a session folder into the trash. A rename is all or nothing: while anything in the
|
|
26689
|
+
* folder is held open (a process's working directory, an open file), Windows refuses it and
|
|
26690
|
+
* the folder stays exactly as it was (`worktree_in_use`). Returns null when the trash is on
|
|
26691
|
+
* another volume.
|
|
26692
|
+
*/
|
|
26693
|
+
async function moveToTrash(home, path) {
|
|
26694
|
+
const trash = trashDir(home);
|
|
26695
|
+
await mkdir(trash, { recursive: true });
|
|
26696
|
+
const stamp = `${Date.now().toString(36)}${Math.random().toString(36).slice(2, 6)}`;
|
|
26697
|
+
const dest = join(trash, `${basename(dirname(path))}-${basename(path)}-${stamp}`);
|
|
26698
|
+
try {
|
|
26699
|
+
await retry(() => rename(path, dest), 8);
|
|
26700
|
+
return dest;
|
|
26701
|
+
} catch (error) {
|
|
26702
|
+
if (errorCode(error) === "EXDEV") return null;
|
|
26703
|
+
throw deleteFailed(path, error);
|
|
26704
|
+
}
|
|
26705
|
+
}
|
|
26706
|
+
const runs = /* @__PURE__ */ new Map();
|
|
26707
|
+
/**
|
|
26708
|
+
* Delete everything in the trash, one folder at a time, never following a link (the links in
|
|
26709
|
+
* a trashed pnpm install still point at the old path, where a new worktree may live by now).
|
|
26710
|
+
* Runs in the background after each removal and when the runner starts; a folder something
|
|
26711
|
+
* still holds open stays for the next run. Calls during a run share it and add one more pass.
|
|
26712
|
+
*/
|
|
26713
|
+
function emptyTrash(home, log) {
|
|
26714
|
+
const trash = trashDir(home);
|
|
26715
|
+
const current = runs.get(trash);
|
|
26716
|
+
if (current) {
|
|
26717
|
+
current.again = true;
|
|
26718
|
+
return current.done;
|
|
26719
|
+
}
|
|
26720
|
+
const run = {
|
|
26721
|
+
again: true,
|
|
26722
|
+
done: Promise.resolve()
|
|
26723
|
+
};
|
|
26724
|
+
run.done = (async () => {
|
|
26725
|
+
try {
|
|
26726
|
+
while (run.again) {
|
|
26727
|
+
run.again = false;
|
|
26728
|
+
const entries = await readdir(trash).catch(() => []);
|
|
26729
|
+
for (const name of entries) await deleteTree(join(trash, name)).catch((error) => log?.(`worktree trash: ${error instanceof Error ? error.message : String(error)}`));
|
|
26730
|
+
}
|
|
26731
|
+
} finally {
|
|
26732
|
+
runs.delete(trash);
|
|
26733
|
+
}
|
|
26734
|
+
})();
|
|
26735
|
+
runs.set(trash, run);
|
|
26736
|
+
return run.done;
|
|
26737
|
+
}
|
|
26738
|
+
/**
|
|
26739
|
+
* Delete a worktree folder that has been cleared for removal. A session folder under
|
|
26740
|
+
* `<home>/wt` goes to the trash, so the removal is instant and the path is free right away,
|
|
26741
|
+
* and is deleted in the background. Anything else is deleted in place.
|
|
26742
|
+
*/
|
|
26743
|
+
async function discardFolder(home, path, log) {
|
|
26744
|
+
if (isManagedWorktreePath(home, path) ? await moveToTrash(home, path) : null) emptyTrash(home, log);
|
|
26745
|
+
else await deleteTree(path);
|
|
26746
|
+
}
|
|
26747
|
+
// ../runner/src/workspace/removal.ts
|
|
26748
|
+
async function canonical(path) {
|
|
26749
|
+
const real = await realpath(path).catch(() => resolve(path));
|
|
26750
|
+
return process.platform === "win32" ? real.toLowerCase() : real;
|
|
26751
|
+
}
|
|
26752
|
+
async function listWorktrees(repo) {
|
|
26753
|
+
const out = await gitText(repo, [
|
|
26754
|
+
"worktree",
|
|
26755
|
+
"list",
|
|
26756
|
+
"--porcelain"
|
|
26757
|
+
]).catch(() => "");
|
|
26758
|
+
const list = [];
|
|
26759
|
+
for (const block of out.split(/\r?\n\r?\n/)) {
|
|
26760
|
+
const lines = block.split(/\r?\n/);
|
|
26761
|
+
if (!lines[0]?.startsWith("worktree ")) continue;
|
|
26762
|
+
const locked = lines.some((l) => l === "locked" || l.startsWith("locked "));
|
|
26763
|
+
list.push({
|
|
26764
|
+
path: lines[0].slice(9),
|
|
26765
|
+
locked
|
|
26766
|
+
});
|
|
26767
|
+
}
|
|
26768
|
+
return list;
|
|
26769
|
+
}
|
|
26770
|
+
/** Classify `path` against the worktrees of `repo` (see `WorktreeState`). */
|
|
26771
|
+
async function worktreeState(repo, path) {
|
|
26772
|
+
const target = await canonical(path);
|
|
26773
|
+
let registered = false;
|
|
26774
|
+
let locked = false;
|
|
26775
|
+
let containsOther = false;
|
|
26776
|
+
for (const wt of await listWorktrees(repo)) {
|
|
26777
|
+
const other = await canonical(wt.path);
|
|
26778
|
+
if (other === target) {
|
|
26779
|
+
registered = true;
|
|
26780
|
+
locked = wt.locked;
|
|
26781
|
+
} else if (isInside(target, other)) containsOther = true;
|
|
26782
|
+
}
|
|
26783
|
+
if (isInside(target, await canonical(repo))) containsOther = true;
|
|
26784
|
+
const info = {
|
|
26785
|
+
registered,
|
|
26786
|
+
locked,
|
|
26787
|
+
containsOther
|
|
26788
|
+
};
|
|
26789
|
+
const stat = await lstat(path).catch(() => void 0);
|
|
26790
|
+
if (!stat) return {
|
|
26791
|
+
state: "missing",
|
|
26792
|
+
...info
|
|
26793
|
+
};
|
|
26794
|
+
if (stat.isSymbolicLink() || !stat.isDirectory()) return {
|
|
26795
|
+
state: "orphan",
|
|
26796
|
+
...info
|
|
26797
|
+
};
|
|
26798
|
+
const dotGit = await lstat(resolve(path, ".git")).catch(() => void 0);
|
|
26799
|
+
if (!dotGit) return {
|
|
26800
|
+
state: "orphan",
|
|
26801
|
+
...info
|
|
26802
|
+
};
|
|
26803
|
+
return {
|
|
26804
|
+
state: dotGit.isFile() && registered ? "registered" : "foreign",
|
|
26805
|
+
...info
|
|
26806
|
+
};
|
|
26807
|
+
}
|
|
26808
|
+
/** Whether a registered worktree has uncommitted changes (ignored files don't count). */
|
|
26809
|
+
async function isDirty(path) {
|
|
26810
|
+
return (await git(path, ["status", "--porcelain"])).stdout.toString("utf8").trim() !== "";
|
|
26811
|
+
}
|
|
26812
|
+
/**
|
|
26813
|
+
* Decide whether `workspace.remove` may delete the path, before anything is touched (terminals
|
|
26814
|
+
* and processes in it are stopped only after this passes). Refuses (`not_a_worktree`) anything
|
|
26815
|
+
* that isn't a linked worktree of the repository, unless it is an orphan inside CodeDen's
|
|
26816
|
+
* worktree folder, and anything containing the repository or another worktree. Unless forced,
|
|
26817
|
+
* keeps a locked worktree (`worktree_locked`) or one with uncommitted changes
|
|
26818
|
+
* (`worktree_dirty`); ignored files (build output, `node_modules`) don't count.
|
|
26819
|
+
*/
|
|
26820
|
+
async function assertRemovable(cmd, home) {
|
|
26821
|
+
const path = resolve(cmd.path);
|
|
26822
|
+
const info = await worktreeState(cmd.repoPath, path);
|
|
26823
|
+
const leftAlone = (why) => new CommandError("not_a_worktree", `${path} ${why}, so it was left alone`);
|
|
26824
|
+
if (info.state === "missing") return info;
|
|
26825
|
+
if (info.containsOther) throw leftAlone("contains the repository or another worktree");
|
|
26826
|
+
if (info.state === "foreign") throw leftAlone("is not a worktree of this repository");
|
|
26827
|
+
if (info.state === "orphan" && !isManagedWorktreePath(home, path)) throw leftAlone("is no longer a git worktree and is outside CodeDen's worktree folder");
|
|
26828
|
+
if (info.state !== "registered" || cmd.force) return info;
|
|
26829
|
+
if (info.locked) throw new CommandError("worktree_locked", `${path} is locked; unlock it or remove it with force`);
|
|
26830
|
+
if (await isDirty(path)) throw new CommandError("worktree_dirty", `${path} has uncommitted changes; commit them or remove it with force`);
|
|
26831
|
+
return info;
|
|
26832
|
+
}
|
|
26833
|
+
/** Drop git's records of worktrees whose folders are gone. */
|
|
26834
|
+
async function pruneWorktrees(repo) {
|
|
26835
|
+
await git(repo, ["worktree", "prune"]).catch(() => void 0);
|
|
26836
|
+
}
|
|
26837
|
+
/**
|
|
26838
|
+
* `workspace.remove`: delete a session worktree and git's record of it. Idempotent: a missing
|
|
26839
|
+
* path, or a half-deleted orphan inside `<home>/wt`, ends removed too. The folder goes to the
|
|
26840
|
+
* trash or is deleted in place, never through a link (`discardFolder`), then
|
|
26841
|
+
* `git worktree prune` drops the record. The branch stays unless `deleteBranch` names it.
|
|
26842
|
+
*/
|
|
26843
|
+
async function removeWorkspace(cmd, home, log) {
|
|
26844
|
+
const path = resolve(cmd.path);
|
|
26845
|
+
const info = await assertRemovable(cmd, home);
|
|
26846
|
+
const registered = info.state === "registered";
|
|
26847
|
+
const discardedChanges = registered && cmd.force === true && await isDirty(path);
|
|
26848
|
+
try {
|
|
26849
|
+
if (registered && info.locked) await git(cmd.repoPath, [
|
|
26850
|
+
"worktree",
|
|
26851
|
+
"unlock",
|
|
26852
|
+
path
|
|
26853
|
+
]).catch(() => void 0);
|
|
26854
|
+
if (info.state !== "missing") await discardFolder(home, path, log);
|
|
26855
|
+
} finally {
|
|
26856
|
+
await pruneWorktrees(cmd.repoPath);
|
|
26857
|
+
}
|
|
26858
|
+
if (cmd.deleteBranch) await git(cmd.repoPath, [
|
|
26859
|
+
"branch",
|
|
26860
|
+
"-D",
|
|
26861
|
+
cmd.deleteBranch
|
|
26862
|
+
]).catch(() => void 0);
|
|
26863
|
+
return {
|
|
26864
|
+
removed: true,
|
|
26865
|
+
...discardedChanges ? { discardedChanges } : {}
|
|
26866
|
+
};
|
|
26867
|
+
}
|
|
26299
26868
|
// ../runner/src/workspace/project-config.ts
|
|
26300
26869
|
const PROJECT_CONFIG_PATH = ".codeden/project.json";
|
|
26301
26870
|
/**
|
|
@@ -26438,20 +27007,41 @@ async function resolveBase(repo, cmd) {
|
|
|
26438
27007
|
])) return `origin/${def}`;
|
|
26439
27008
|
return def ?? "HEAD";
|
|
26440
27009
|
}
|
|
26441
|
-
|
|
27010
|
+
function isEmptyDir(path) {
|
|
27011
|
+
try {
|
|
27012
|
+
return readdirSync(path).length === 0;
|
|
27013
|
+
} catch {
|
|
27014
|
+
return false;
|
|
27015
|
+
}
|
|
27016
|
+
}
|
|
27017
|
+
/**
|
|
27018
|
+
* Make `path` ready for `git worktree add`: reuse a worktree of this repository, delete the
|
|
27019
|
+
* orphan folder an interrupted removal left inside `<home>/wt` (stopping processes in it
|
|
27020
|
+
* first), and drop git's stale record of it. Returns the worktree to reuse, if any.
|
|
27021
|
+
*/
|
|
27022
|
+
async function clearTarget(repo, path, ctx) {
|
|
27023
|
+
const info = await worktreeState(repo, path);
|
|
27024
|
+
if (info.state === "registered") return {
|
|
27025
|
+
cwd: path,
|
|
27026
|
+
branch: await currentBranch(path),
|
|
27027
|
+
created: false
|
|
27028
|
+
};
|
|
27029
|
+
if (info.state === "orphan" && isManagedWorktreePath(ctx.home, path) && !info.containsOther) {
|
|
27030
|
+
await ctx.supervisor.killUnder(path);
|
|
27031
|
+
await discardFolder(ctx.home, path);
|
|
27032
|
+
} else if (info.state !== "missing" && !isEmptyDir(path)) throw new CommandError("path_exists", `${path} exists and is not a worktree`);
|
|
27033
|
+
if (info.registered) await pruneWorktrees(repo);
|
|
27034
|
+
return null;
|
|
27035
|
+
}
|
|
27036
|
+
async function createWorktree(cmd, ctx) {
|
|
26442
27037
|
const repo = await topLevel(cmd.repoPath).catch(() => {
|
|
26443
27038
|
throw new CommandError("not_a_repo", `${cmd.repoPath} is not a git repository`);
|
|
26444
27039
|
});
|
|
26445
27040
|
const short = sessionShort(cmd.sessionId);
|
|
26446
|
-
const
|
|
26447
|
-
|
|
26448
|
-
|
|
26449
|
-
|
|
26450
|
-
branch: await currentBranch(path),
|
|
26451
|
-
created: false
|
|
26452
|
-
};
|
|
26453
|
-
if (readdirSync(path).length > 0) throw new CommandError("path_exists", `${path} exists and is not a worktree`);
|
|
26454
|
-
}
|
|
27041
|
+
const slug = cmd.projectSlug ?? slugFromPath(repo);
|
|
27042
|
+
const path = resolve(cmd.path ?? join(ctx.home, "wt", slug, short));
|
|
27043
|
+
const reuse = await clearTarget(repo, path, ctx);
|
|
27044
|
+
if (reuse) return reuse;
|
|
26455
27045
|
await mkdir(dirname(path), { recursive: true });
|
|
26456
27046
|
const base = await resolveBase(repo, cmd);
|
|
26457
27047
|
if (cmd.branch && await branchExists(repo, cmd.branch)) {
|
|
@@ -26511,7 +27101,7 @@ async function prepareWorkspace(cmd, ctx) {
|
|
|
26511
27101
|
created: false
|
|
26512
27102
|
};
|
|
26513
27103
|
break;
|
|
26514
|
-
case "worktree": target = await createWorktree(cmd, ctx
|
|
27104
|
+
case "worktree": target = await createWorktree(cmd, ctx);
|
|
26515
27105
|
}
|
|
26516
27106
|
const { config, error } = await readProjectConfig(target.cwd);
|
|
26517
27107
|
if (error) ctx.emit({
|
|
@@ -26536,53 +27126,6 @@ async function prepareWorkspace(cmd, ctx) {
|
|
|
26536
27126
|
setup
|
|
26537
27127
|
};
|
|
26538
27128
|
}
|
|
26539
|
-
/**
|
|
26540
|
-
* Refuse to remove a worktree with uncommitted changes (`worktree_dirty`) unless forced. Runs
|
|
26541
|
-
* before terminals and processes in the worktree are stopped, so a dirty worktree is left
|
|
26542
|
-
* exactly as it was. Ignored files (build output, `node_modules`) don't count.
|
|
26543
|
-
*/
|
|
26544
|
-
async function assertRemovable(cmd) {
|
|
26545
|
-
if (cmd.force || !existsSync(cmd.path) || !await isWorkTree(cmd.path)) return;
|
|
26546
|
-
if ((await git(cmd.path, ["status", "--porcelain"])).stdout.toString("utf8").trim()) throw new CommandError("worktree_dirty", `${cmd.path} has uncommitted changes; commit them or remove it with force`);
|
|
26547
|
-
}
|
|
26548
|
-
/** `workspace.remove`: remove a worktree (retrying briefly for Windows file locks). */
|
|
26549
|
-
async function removeWorkspace(cmd) {
|
|
26550
|
-
const path = resolve(cmd.path);
|
|
26551
|
-
let removed = false;
|
|
26552
|
-
let lastError;
|
|
26553
|
-
for (let attempt = 0; attempt < 5 && !removed; attempt++) try {
|
|
26554
|
-
const args = [
|
|
26555
|
-
"worktree",
|
|
26556
|
-
"remove",
|
|
26557
|
-
...cmd.force ? ["--force"] : [],
|
|
26558
|
-
path
|
|
26559
|
-
];
|
|
26560
|
-
await git(cmd.repoPath, args);
|
|
26561
|
-
removed = true;
|
|
26562
|
-
} catch (error) {
|
|
26563
|
-
lastError = error;
|
|
26564
|
-
if (!existsSync(path)) break;
|
|
26565
|
-
if (!cmd.force && /modified or untracked|contains/.test(String(error))) break;
|
|
26566
|
-
await new Promise((r) => setTimeout(r, 250 * (attempt + 1)));
|
|
26567
|
-
}
|
|
26568
|
-
if (!removed && existsSync(path) && cmd.force) {
|
|
26569
|
-
await rm(path, {
|
|
26570
|
-
recursive: true,
|
|
26571
|
-
force: true,
|
|
26572
|
-
maxRetries: 5,
|
|
26573
|
-
retryDelay: 200
|
|
26574
|
-
});
|
|
26575
|
-
removed = true;
|
|
26576
|
-
}
|
|
26577
|
-
await git(cmd.repoPath, ["worktree", "prune"]).catch(() => void 0);
|
|
26578
|
-
if (!removed && existsSync(path)) throw lastError instanceof Error ? lastError : new CommandError("remove_failed", `Could not remove ${path}`);
|
|
26579
|
-
if (cmd.deleteBranch) await git(cmd.repoPath, [
|
|
26580
|
-
"branch",
|
|
26581
|
-
"-D",
|
|
26582
|
-
cmd.deleteBranch
|
|
26583
|
-
]).catch(() => void 0);
|
|
26584
|
-
return { removed: removed || !existsSync(path) };
|
|
26585
|
-
}
|
|
26586
27129
|
// ../runner/src/commands.ts
|
|
26587
27130
|
/** Execute one server command (§4.3). Throws `CommandError` for expected failures. */
|
|
26588
27131
|
async function executeCommand(core, command) {
|
|
@@ -26632,10 +27175,10 @@ async function executeCommand(core, command) {
|
|
|
26632
27175
|
emit: core.emit
|
|
26633
27176
|
});
|
|
26634
27177
|
case "workspace.remove":
|
|
26635
|
-
await assertRemovable(command);
|
|
27178
|
+
await assertRemovable(command, core.home);
|
|
26636
27179
|
await terminals.closeUnder(command.path);
|
|
26637
27180
|
await core.supervisor.killUnder(command.path);
|
|
26638
|
-
return removeWorkspace(command);
|
|
27181
|
+
return removeWorkspace(command, core.home, core.log);
|
|
26639
27182
|
case "session.start":
|
|
26640
27183
|
if (core.isDraining()) throw new CommandError("draining", "Runner is draining");
|
|
26641
27184
|
return host.start(command);
|
|
@@ -26913,12 +27456,6 @@ async function handleMcpRequest(request, upstream) {
|
|
|
26913
27456
|
return jsonRpcError(502, `CodeDen server unreachable: ${error.message}`);
|
|
26914
27457
|
}
|
|
26915
27458
|
}
|
|
26916
|
-
// ../runner/src/util/paths.ts
|
|
26917
|
-
/** True when `child` is `dir` itself or somewhere inside it. */
|
|
26918
|
-
function isInside(dir, child) {
|
|
26919
|
-
const rel = relative(resolve(dir), resolve(child));
|
|
26920
|
-
return rel === "" || !rel.startsWith("..") && !/^[a-zA-Z]:/.test(rel);
|
|
26921
|
-
}
|
|
26922
27459
|
// ../runner/src/process/supervisor.ts
|
|
26923
27460
|
/**
|
|
26924
27461
|
* Tracks every child process the runner starts so they can be killed as whole process
|
|
@@ -28721,6 +29258,7 @@ async function startRunner(opts) {
|
|
|
28721
29258
|
browser,
|
|
28722
29259
|
startedAt: (/* @__PURE__ */ new Date()).toISOString(),
|
|
28723
29260
|
emit,
|
|
29261
|
+
log,
|
|
28724
29262
|
isDraining: () => draining,
|
|
28725
29263
|
drain: ({ reason, timeoutMs, exitWhenDrained }) => {
|
|
28726
29264
|
if (!draining) {
|
|
@@ -28794,6 +29332,7 @@ async function startRunner(opts) {
|
|
|
28794
29332
|
renameSync(tmp, lockFile);
|
|
28795
29333
|
log(`runner ${journal.runnerId} listening on 127.0.0.1:${port}`);
|
|
28796
29334
|
registry.refreshModels();
|
|
29335
|
+
emptyTrash(opts.home, log);
|
|
28797
29336
|
const removeLock = () => removeOwnLock(lockFile);
|
|
28798
29337
|
process.once("exit", removeLock);
|
|
28799
29338
|
async function close() {
|
|
@@ -28824,6 +29363,6 @@ async function startRunner(opts) {
|
|
|
28824
29363
|
close
|
|
28825
29364
|
};
|
|
28826
29365
|
}
|
|
28827
|
-
export {
|
|
29366
|
+
export { ExportAutomationsQuery as $, datetime as $n, TriageIssueRequest as $t, SessionFileQuery as A, SessionOptions as An, UiClientMessage as At, ImageTestRequest as B, InboxItemKind as Bn, UpdateInstallRequest as Bt, isElectronRuntime as C, GitPushRequest as Cn, SecretKey as Ct, CODEDEN_VERSION as D, TerminalClientMessage as Dn, StartSessionRequest as Dt, wrapper_default as E, SpeechSummaryRequest as En, SetSecretRequest as Et, ListConflictsQuery as F, Project as Fn, LOCAL_STT_SAMPLE_RATE as Ft, BrowserNavigateRequest as G, AgentConfigLocator as Gn, NOTICE_BATCH_CODE as Gt, BrowserClientMessage as H, AgentConfigDeleteQuery as Hn, compareVersions$1 as Ht, ListNoticesQuery as I, sortProjects as In, LocalSttModelParams as It, BrowserViewportRequest as J, McpServerTarget as Jn, GithubIssue as Jt, BrowserNewTabRequest as K, AgentConfigWriteRequest as Kn, CreateGithubIssueRequest as Kt, ResolveConflictRequest as L, DEFAULT_VISION_PATH as Ln, SttQuery as Lt, RunnerToServerMessage as M, ConflictDecision as Mn, HealthResponse as Mt, ServerLock as N, StartedBy as Nn, agentEventToNewEvent as Nt, UsageQuery as O, TerminalQuery as On, SteerRequest as Ot, ArchiveSessionRequest as P, WorkKey as Pn, Settings as Pt, DuplicateAutomationRequest as Q, ReasoningEffort as Qn, MergePrRequest as Qt, RoleMessageRequest as R, FactoryLimits as Rn, TtsRequest as Rt, acquireInstallLock as S, GitCommitRequest as Sn, RespondRequest as St, import_websocket_server as T, SessionResult as Tn, SessionStartMeta as Tt, BrowserDevServerRequest as U, AgentConfigDiffRequest as Un, isNewerVersion as Ut, summarizeActivity as V, messageFrom as Vn, UpdateStatus as Vt, BrowserHistoryRequest as W, AgentConfigListQuery as Wn, ConflictRecord as Wt, AUTOMATION_IMPORT_LIMITS as X, AutonomyLevel as Xn, GithubListQuery as Xt, BrowserWsQuery as Y, McpServerUpsertRequest as Yn, GithubLabelRequest as Yt, AutomationImportSource as Z, ProviderId as Zn, GithubPr as Zt, importClaudeSdkFrom as _, TriagePolicy as _n, unknown as _r, FileWriteRequest as _t, Hono as a, FactoryTuning as an, discriminatedUnion as ar, ListAutomationRunsQuery as at, runNpm as b, DiffScope as bn, safeParse$1 as br, ReorderProjectsRequest as bt, serve as c, ReviewVerdict as cn, literal as cr, SetAutomationEnabledRequest as ct, createImageEngineRegistry as d, Artifact as dn, object as dr, TriageReport as dt, githubPrId as en, _enum as er, ImportAutomationsRequest as et, IMAGE_ASPECT_RATIOS as f, Automation as fn, optional as fr, UpcomingRunsQuery as ft, CLAUDE_SDK_VERSION as g, AutomationTrigger as gn, union as gr, FileReadQuery as gt, claudeSdkSource as h, AutomationRun as hn, string as hr, CreateProjectRequest as ht, killTree as i, FactoryTriageRequest as in, custom as ir, CreateAutomationRequest as it, RunnerLock as j, ProjectConfigFile as jn, UpdateProjectRequest as jt, UsageSettings as k, RunnerCommandResults as kn, ThreadItemsQuery as kt, upgradeWebSocket as l, SetAutonomyModeRequest as ln, looseObject as lr, StarterSetRequest as lt, ImageGenerationError as m, AutomationGuards as mn, record as mr, UpdateAutomationRequest as mt, readLock as n, DiscoveryArea as nn, array as nr, AutomationGuardsPatch as nt, tryDecodeURIComponent as o, KillSwitchRequest as on, int as or, ListAutomationsQuery as ot, resolveImageOutput as p, AutomationAction as pn, preprocess as pr, UpdateAutomationProjectSettingsRequest as pt, BrowserOpenRequest as q, McpServerDiffRequest as qn, GithubCommentRequest as qt, isAlive as r, FactoryCandidate as rn, boolean as rr, AutomationProjectRequest as rt, bufferToFormData as s, PutVisionRequest as sn, intersection as sr, RunAutomationRequest as st, startRunner as t, parseGithubPrId as tn, _null as tr, AutomationFileFrontmatter as tt, ImageEngineRegistry as u, isFactoryMode as un, number$1 as ur, TriageDecisionRequest as ut, installClaudeSdk as v, Session as vn, ZodError as vr, ListInboxQuery as vt, ownNodeEnv as w, RestoreFilesRequest as wn, SendMessageRequest as wt, resolveNpm as x, EditQueuedMessageRequest as xn, prettifyError as xr, ResolveInboxRequest as xt, isClaudeSdkInstalled as y, SessionStatus as yn, toJSONSchema as yr, ListSessionsQuery as yt, UpdateSessionRequest as z, InboxItem as zn, AppPointer as zt };
|
|
28828
29367
|
|
|
28829
|
-
//# sourceMappingURL=runner-
|
|
29368
|
+
//# sourceMappingURL=runner-DGomZl80.js.map
|