toolroll 0.9.82 → 0.9.83
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/CHANGELOG.md +14 -0
- package/dist/desktop-update.d.ts +1 -1
- package/dist/first-tasks-child.d.ts +1 -0
- package/dist/first-tasks-child.js +11 -0
- package/dist/folder-look.d.ts +107 -0
- package/dist/folder-look.js +302 -0
- package/dist/learning-child.d.ts +1 -0
- package/dist/learning-child.js +34 -0
- package/dist/learning-look.d.ts +25 -0
- package/dist/learning-look.js +72 -0
- package/dist/operate.d.ts +32 -0
- package/dist/operate.js +186 -28
- package/dist/project-identity.d.ts +37 -0
- package/dist/project-identity.js +106 -0
- package/dist/project-knowledge.d.ts +6 -6
- package/dist/project-tools.d.ts +4 -1
- package/dist/project-tools.js +7 -2
- package/dist/project.d.ts +4 -1
- package/dist/project.js +11 -4
- package/dist/repos.d.ts +7 -0
- package/dist/repos.js +57 -0
- package/dist/serve.js +35 -8
- package/dist/server/flows.js +9 -3
- package/dist/server/guards.d.ts +4 -0
- package/dist/server/guards.js +12 -3
- package/dist/server/http.d.ts +11 -0
- package/dist/server/http.js +3 -0
- package/dist/server/pages.js +62 -88
- package/dist/server/render-pages.d.ts +2 -16
- package/dist/server/render-pages.js +1 -55
- package/dist/server/runtime.d.ts +4 -0
- package/dist/server/runtime.js +1 -0
- package/dist/server/settings.js +14 -3
- package/dist/server/tasks.js +7 -3
- package/dist/up-processes.d.ts +13 -0
- package/dist/up-processes.js +6 -2
- package/package.json +1 -1
|
@@ -422,22 +422,6 @@ export type ProjectPeek = {
|
|
|
422
422
|
* GitHub identity. Everything else is null.
|
|
423
423
|
*/
|
|
424
424
|
export declare function githubIdentityOf(remoteUrl: string): string | null;
|
|
425
|
-
/**
|
|
426
|
-
* The `origin` remote's url from a repository's OWN .git/config. BOUNDED
|
|
427
|
-
* I/O by construction (ghlist review, finding 1): the final component may
|
|
428
|
-
* not be a symlink (O_NOFOLLOW + lstat), must be a REGULAR file under a
|
|
429
|
-
* size cap, and at most 64 KiB are ever read — a sparse monster or a fifo
|
|
430
|
-
* planted as a "config" reads as null, never as a hang. Parsed LINE BY
|
|
431
|
-
* LINE with real section tracking (finding 2): a `[remote "origin"]`
|
|
432
|
-
* embedded inside some other value never opens the section. null when
|
|
433
|
-
* unreadable or origin-less; a worktree-style `.git` FILE (gitdir pointer)
|
|
434
|
-
* reads null too, which is honest — its identity lives elsewhere. The
|
|
435
|
-
* answer is ADVISORY metadata for offering a button: the /projects/open
|
|
436
|
-
* road re-proves path, ceiling, and git-ness before anything mutates, and
|
|
437
|
-
* a config that lies about its origin can only mislabel a repository the
|
|
438
|
-
* operator was already allowed to open.
|
|
439
|
-
*/
|
|
440
|
-
export declare function originUrlOf(repoPath: string): string | null;
|
|
441
425
|
/** The GitHub listing page: the account's repositories with ONE honest
|
|
442
426
|
* action each. Names, descriptions, and paths are gh/filesystem DATA —
|
|
443
427
|
* escaped at the sink like everything else. */
|
|
@@ -450,6 +434,8 @@ export declare function githubReposPage(chrome: Chrome, data: {
|
|
|
450
434
|
csrf: string;
|
|
451
435
|
cloneReady: boolean;
|
|
452
436
|
openProject: string | null;
|
|
437
|
+
/** A projects folder didn't answer in time: local copies weren't matched this time. */
|
|
438
|
+
localUnanswered?: boolean;
|
|
453
439
|
}): Screen;
|
|
454
440
|
export declare function projectsPage(chrome: Chrome, recent: {
|
|
455
441
|
path: string;
|
|
@@ -22,7 +22,6 @@ import { runCostWords, spendLine, tally } from "../summary.js";
|
|
|
22
22
|
import {} from "../work-index.js";
|
|
23
23
|
import { WORK_VIEWS } from "../workspace-ui.js";
|
|
24
24
|
import { createHash } from "node:crypto";
|
|
25
|
-
import { closeSync, constants as fsConstants, lstatSync, openSync, readSync } from "node:fs";
|
|
26
25
|
import { homedir } from "node:os";
|
|
27
26
|
import { join } from "node:path";
|
|
28
27
|
import { buildsViews, CHEVRON_ICON, consoleSpend, FOLDER_PATHS, GRIP_HANDLE, isOverdue, projectChip, relativeAge, safePrUrl, screen, sentenceCase, strokeIcon, TO_FRONT_ICON, when, whenTime } from "./chrome.js";
|
|
@@ -865,60 +864,6 @@ export function githubIdentityOf(remoteUrl) {
|
|
|
865
864
|
return `${ssh[1]}/${ssh[2]}`;
|
|
866
865
|
return null;
|
|
867
866
|
}
|
|
868
|
-
/**
|
|
869
|
-
* The `origin` remote's url from a repository's OWN .git/config. BOUNDED
|
|
870
|
-
* I/O by construction (ghlist review, finding 1): the final component may
|
|
871
|
-
* not be a symlink (O_NOFOLLOW + lstat), must be a REGULAR file under a
|
|
872
|
-
* size cap, and at most 64 KiB are ever read — a sparse monster or a fifo
|
|
873
|
-
* planted as a "config" reads as null, never as a hang. Parsed LINE BY
|
|
874
|
-
* LINE with real section tracking (finding 2): a `[remote "origin"]`
|
|
875
|
-
* embedded inside some other value never opens the section. null when
|
|
876
|
-
* unreadable or origin-less; a worktree-style `.git` FILE (gitdir pointer)
|
|
877
|
-
* reads null too, which is honest — its identity lives elsewhere. The
|
|
878
|
-
* answer is ADVISORY metadata for offering a button: the /projects/open
|
|
879
|
-
* road re-proves path, ceiling, and git-ness before anything mutates, and
|
|
880
|
-
* a config that lies about its origin can only mislabel a repository the
|
|
881
|
-
* operator was already allowed to open.
|
|
882
|
-
*/
|
|
883
|
-
export function originUrlOf(repoPath) {
|
|
884
|
-
const file = join(repoPath, ".git", "config");
|
|
885
|
-
let fd = null;
|
|
886
|
-
try {
|
|
887
|
-
const stat = lstatSync(file);
|
|
888
|
-
if (!stat.isFile() || stat.size > 1024 * 1024)
|
|
889
|
-
return null;
|
|
890
|
-
fd = openSync(file, fsConstants.O_RDONLY | fsConstants.O_NOFOLLOW);
|
|
891
|
-
const buffer = Buffer.alloc(64 * 1024);
|
|
892
|
-
const read = readSync(fd, buffer, 0, buffer.length, 0);
|
|
893
|
-
const config = buffer.toString("utf8", 0, read);
|
|
894
|
-
let inOrigin = false;
|
|
895
|
-
for (const line of config.split("\n")) {
|
|
896
|
-
if (/^\s*\[/.test(line)) {
|
|
897
|
-
inOrigin = /^\s*\[remote "origin"\]\s*$/.test(line);
|
|
898
|
-
continue;
|
|
899
|
-
}
|
|
900
|
-
if (!inOrigin)
|
|
901
|
-
continue;
|
|
902
|
-
const url = /^\s*url\s*=\s*(.+)$/.exec(line)?.[1];
|
|
903
|
-
if (url !== undefined)
|
|
904
|
-
return url.trim();
|
|
905
|
-
}
|
|
906
|
-
return null;
|
|
907
|
-
}
|
|
908
|
-
catch {
|
|
909
|
-
return null;
|
|
910
|
-
}
|
|
911
|
-
finally {
|
|
912
|
-
if (fd !== null) {
|
|
913
|
-
try {
|
|
914
|
-
closeSync(fd);
|
|
915
|
-
}
|
|
916
|
-
catch {
|
|
917
|
-
// already closed
|
|
918
|
-
}
|
|
919
|
-
}
|
|
920
|
-
}
|
|
921
|
-
}
|
|
922
867
|
/** The GitHub listing page: the account's repositories with ONE honest
|
|
923
868
|
* action each. Names, descriptions, and paths are gh/filesystem DATA —
|
|
924
869
|
* escaped at the sink like everything else. */
|
|
@@ -955,6 +900,7 @@ export function githubReposPage(chrome, data) {
|
|
|
955
900
|
return screen("projects", joinHtml([
|
|
956
901
|
html `<h1>Your GitHub repositories</h1>`,
|
|
957
902
|
html `<p class="meta">Repositories available to the GitHub account signed in on this machine — open one you already have, or clone a new one after a quick preview.</p>`,
|
|
903
|
+
data.localUnanswered === true ? html `<p class="meta">Your projects folder didn't answer, so copies already on this machine aren't matched yet. Try again in a moment.</p>` : null,
|
|
958
904
|
rows,
|
|
959
905
|
data.listed === null ? null : list("Refresh list"),
|
|
960
906
|
html `<p class="row" style="margin-top:.6rem"><a class="badge" href="/projects">← back to projects</a></p>`,
|
package/dist/server/runtime.d.ts
CHANGED
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { type ProjectIdentities } from "../project-identity.js";
|
|
1
2
|
import type { FreshFacts } from '../scope.js';
|
|
2
3
|
import type { createPersonalQueueRuntime } from '../lead-queue-runtime.js';
|
|
3
4
|
import type { FlowRooms } from '../flow-live.js';
|
|
@@ -95,7 +96,10 @@ export interface ServerRuntime {
|
|
|
95
96
|
liveCeiling: () => {
|
|
96
97
|
repos: string[];
|
|
97
98
|
roots: readonly string[];
|
|
99
|
+
canon?: (path: string) => string | null;
|
|
98
100
|
};
|
|
101
|
+
/** Project identities without reading folders on the console's thread (project-identity.ts). */
|
|
102
|
+
identities: ProjectIdentities;
|
|
99
103
|
taskScreen: (response: ServerResponse, who: Who, taskId: string, problem: string | null, status: number, scopeDraft?: URLSearchParams, cancelDraft?: string, editPlan?: boolean, page?: TaskPageExtras) => void;
|
|
100
104
|
runVisible: (run: Run) => boolean;
|
|
101
105
|
runIsLive: (run: Pick<Run, "outcome" | "leaseId" | "taskRef">) => boolean;
|
package/dist/server/runtime.js
CHANGED
package/dist/server/settings.js
CHANGED
|
@@ -9,6 +9,7 @@ import { CONNECT_CALLBACK, connectionsOf, finishConnect, localConnectOf, oneClic
|
|
|
9
9
|
import { learningView } from "../project-learning.js";
|
|
10
10
|
import { skillTestResult } from "../project-skills.js";
|
|
11
11
|
import { discoverTools, projectToolsOf } from "../project-tools.js";
|
|
12
|
+
import { lookProjectMcp } from "../folder-look.js";
|
|
12
13
|
import { publishingOf } from '../pull-request-flow.js';
|
|
13
14
|
import { skillTestFeedbackHtml } from "../skills-ui.js";
|
|
14
15
|
import { labelOf, nameOf } from "../subagent-admin.js";
|
|
@@ -132,7 +133,7 @@ import { personChip, ROLE_TITLES } from "./chrome.js";
|
|
|
132
133
|
import { chatReturnWithSaid, goOutside } from "./http.js";
|
|
133
134
|
import { leadSettingsHtml, notificationProjects, SETTINGS_AUTOSAVE_SCRIPT, settingsPage, settingsWorkers, telegramDeliveryWords, telegramTrouble } from "./render-settings.js";
|
|
134
135
|
export function createSettingsHandlers(runtime) {
|
|
135
|
-
const { store, providerHome, managedRepos, consoleProjects, sendScreen, chromeFor, connectionCheck, connectionKnown, lastChecks, checkRelease, clock, modelCatalog, modelSeams, toolsViewOf, codexServers, projectViewOf, sessions, restricted, options, integrations, consoleOrigin, evidenceRoot, storagePool, visible, ssoSettings, checkLocalAgents, chatKeyFor, chatCatalog, chatCatalogKnown, leadWords, agentSignInCommand, settingsUpdates, phoneSetup, authenticateApprover, authenticateCredential, deletedRepos, projectOf, providerFor, admissionList, toolHome, connectVisits, mintApprovalNonce, consumeApprovalNonce, defaultProject, googleVisits } = runtime;
|
|
136
|
+
const { identities, store, providerHome, managedRepos, consoleProjects, sendScreen, chromeFor, connectionCheck, connectionKnown, lastChecks, checkRelease, clock, modelCatalog, modelSeams, toolsViewOf, codexServers, projectViewOf, sessions, restricted, options, integrations, consoleOrigin, evidenceRoot, storagePool, visible, ssoSettings, checkLocalAgents, chatKeyFor, chatCatalog, chatCatalogKnown, leadWords, agentSignInCommand, settingsUpdates, phoneSetup, authenticateApprover, authenticateCredential, deletedRepos, projectOf, providerFor, admissionList, toolHome, connectVisits, mintApprovalNonce, consumeApprovalNonce, defaultProject, googleVisits } = runtime;
|
|
136
137
|
// v105: what agent work cost, by month, and the monthly budgets. An instance operator's page.
|
|
137
138
|
async function spendPage(ctx) {
|
|
138
139
|
const { url, who, response, now } = ctx;
|
|
@@ -1547,7 +1548,15 @@ ${repositoryContextHtml(chosen, query, result, who.role === 'approver' ? csrf :
|
|
|
1547
1548
|
const wanted = body.getAll("import");
|
|
1548
1549
|
if (wanted.length === 0)
|
|
1549
1550
|
return back("problem", "Choose at least one tool to add.");
|
|
1550
|
-
|
|
1551
|
+
// The project's own .mcp.json read now, off the console's thread (folder-look.ts).
|
|
1552
|
+
let projectConfig;
|
|
1553
|
+
if (options.identities !== undefined) {
|
|
1554
|
+
const looked = await lookProjectMcp(repo);
|
|
1555
|
+
if (!looked.ok)
|
|
1556
|
+
return back("problem", "This project's folder didn't answer. It may be on a drive that's disconnected, or waiting on macOS. Try again in a moment.");
|
|
1557
|
+
projectConfig = looked.value;
|
|
1558
|
+
}
|
|
1559
|
+
const found = discoverTools(repo, await codexServers(repo), toolHome, process.env, projectConfig);
|
|
1551
1560
|
const added = [], problems = [];
|
|
1552
1561
|
for (const one of wanted) {
|
|
1553
1562
|
const match = found.find(tool => tool.spec.name === one);
|
|
@@ -1887,7 +1896,9 @@ ${repositoryContextHtml(chosen, query, result, who.role === 'approver' ? csrf :
|
|
|
1887
1896
|
if (["scope", "full-access", "cap-usd", "nonce", "digest", "token"].some(key => body.getAll(key).length > 1))
|
|
1888
1897
|
return refuse(response, who, 400, "Submit one value for each field.", "/settings");
|
|
1889
1898
|
const given = (body.get("scope") ?? "all").trim();
|
|
1890
|
-
const scope = given === "all" || given === "" ? CHAT_APPROVAL_ALL :
|
|
1899
|
+
const scope = given === "all" || given === "" ? CHAT_APPROVAL_ALL : identities.vouched(given, consoleProjects) ?? null;
|
|
1900
|
+
if (scope === null)
|
|
1901
|
+
return refuse(response, who, 403, "That isn't one of your projects.", "/settings");
|
|
1891
1902
|
if (scope !== CHAT_APPROVAL_ALL && !(visible(scope) && store.accountCanAccess(who.name, scope)))
|
|
1892
1903
|
return refuse(response, who, 403, "That isn't one of your projects.", "/settings");
|
|
1893
1904
|
const where = scope === CHAT_APPROVAL_ALL ? "all your projects" : projectName(scope);
|
package/dist/server/tasks.js
CHANGED
|
@@ -72,7 +72,7 @@ import { ACCEPT_ANYWAY_NEEDS_REASON, editorFileHref, proofBundleView, rankReview
|
|
|
72
72
|
import { approvalFormDigest, approveRefusalWords, checkProgressHtml, consentDoorOf, decisionPage, nonceHashOf, PermissionsWouldChange, requirementsFromEditor, resumeDigestOf, taskOf } from "./render-tasks.js";
|
|
73
73
|
import {} from "./session.js";
|
|
74
74
|
export function createTasksHandlers(runtime) {
|
|
75
|
-
const { store, peekSay, peekCache, PEEK_CACHE_TTL_MS, peekInFlight, PEEK_GLOBAL_INFLIGHT, peekBySession, PEEK_SESSION_INFLIGHT, clock, peekName, PEEK_FRAGMENT_BYTES, peekEvict, evidenceRoot, sendScreen, chromeFor, visible, consumeApprovalNonce, authenticateApprover, options, mintApprovalNonce, taskScreen, unscopedMode, admissionList, planViewOf, revisionViewOf, runIsTaskResult, leadPrincipal, familyOf, armTaskResume, runIsLive, revisionLedgerOf, resultDetailOf, reviewFactsFor, taskRepoOf, runVisible, restricted, codingActorAllowed, codingProjectAllowed, managedRepos, routeViewOf, workAccess, firstRunStepsNow, revisionDocOf, failureOf, dockedConversation, planContractViewOf, familiesInView, explainAttempt, taskChatFocus, familyTasksInView, liveCeiling, projectOf, ceiling, bustBadge, revisionDestination } = runtime;
|
|
75
|
+
const { identities, store, peekSay, peekCache, PEEK_CACHE_TTL_MS, peekInFlight, PEEK_GLOBAL_INFLIGHT, peekBySession, PEEK_SESSION_INFLIGHT, clock, peekName, PEEK_FRAGMENT_BYTES, peekEvict, evidenceRoot, sendScreen, chromeFor, visible, consumeApprovalNonce, authenticateApprover, options, mintApprovalNonce, taskScreen, unscopedMode, admissionList, planViewOf, revisionViewOf, runIsTaskResult, leadPrincipal, familyOf, armTaskResume, runIsLive, revisionLedgerOf, resultDetailOf, reviewFactsFor, taskRepoOf, runVisible, restricted, codingActorAllowed, codingProjectAllowed, managedRepos, routeViewOf, workAccess, firstRunStepsNow, revisionDocOf, failureOf, dockedConversation, planContractViewOf, familiesInView, explainAttempt, taskChatFocus, familyTasksInView, liveCeiling, projectOf, ceiling, bustBadge, revisionDestination } = runtime;
|
|
76
76
|
/** A row admitted the address but its path parameter does not parse: the console's unknown page. */
|
|
77
77
|
function noPage(who, response) {
|
|
78
78
|
return refuse(response, who, 404, "There's no page at this address.", "/chat");
|
|
@@ -1022,8 +1022,12 @@ export function createTasksHandlers(runtime) {
|
|
|
1022
1022
|
let admitted = unscopedMode ? null : admissionList() ?? [];
|
|
1023
1023
|
const rootMode = !unscopedMode && ceiling.roots.length > 0;
|
|
1024
1024
|
if (rootMode && effective !== "") {
|
|
1025
|
-
|
|
1026
|
-
|
|
1025
|
+
// Resolved off the console's thread (project-identity.ts), then proved against the ceiling as that identity.
|
|
1026
|
+
const resolved = await identities.resolveNow(effective);
|
|
1027
|
+
if (resolved === "no-answer")
|
|
1028
|
+
return newTaskScreen(ctx, 503, `${effective} didn't answer — its folder may be on a drive that's disconnected, or waiting on macOS; try again in a moment`, draft);
|
|
1029
|
+
const canonical = resolved !== null && (await authorizedProject(liveCeiling(), resolved)) ? resolved : null;
|
|
1030
|
+
if (canonical === null) {
|
|
1027
1031
|
return newTaskScreen(ctx, 403, `${effective} is outside what this server was configured to show`, draft);
|
|
1028
1032
|
}
|
|
1029
1033
|
repo = canonical;
|
package/dist/up-processes.d.ts
CHANGED
|
@@ -26,6 +26,14 @@ export type UpWorkerConfig = {
|
|
|
26
26
|
terminal: boolean;
|
|
27
27
|
/** How long a start waits for a predecessor's watch lease to expire (a crashed worker's, after a restart). */
|
|
28
28
|
leaseWaitMs: number;
|
|
29
|
+
/** Saved projects this start couldn't reach (a folder that didn't answer, a drive not connected): looked at again
|
|
30
|
+
* while it runs, and connected once they answer, on the same saved-list authority the start had. */
|
|
31
|
+
deferredRepos?: string[];
|
|
32
|
+
/** Each of those lines' removal count when this start read the saved list: a different count at binding means it was
|
|
33
|
+
* removed since (even if saved again), and the start's authority for it is gone. */
|
|
34
|
+
deferredGenerations?: Record<string, number>;
|
|
35
|
+
/** The saved line each of `repos` was proved from: a project stays while its line is saved, whatever else answers. */
|
|
36
|
+
repoLines?: Record<string, string[]>;
|
|
29
37
|
};
|
|
30
38
|
export type UpRoleStart = {
|
|
31
39
|
type: "start";
|
|
@@ -67,6 +75,11 @@ export type RoleToParent = {
|
|
|
67
75
|
} | {
|
|
68
76
|
type: "repos";
|
|
69
77
|
repos: string[];
|
|
78
|
+
}
|
|
79
|
+
/** The saved projects the worker still holds start authority for (it only ever shrinks: connected or removed). */
|
|
80
|
+
| {
|
|
81
|
+
type: "deferred";
|
|
82
|
+
repos: string[];
|
|
70
83
|
} | {
|
|
71
84
|
type: "failed";
|
|
72
85
|
reason: "port-busy" | "start";
|
package/dist/up-processes.js
CHANGED
|
@@ -53,6 +53,7 @@ export function roleMessage(value, role) {
|
|
|
53
53
|
return { type: "ready", role, build: value["build"], pid: value["pid"], url: value["url"], port: value["port"] };
|
|
54
54
|
case "log": return text(value["line"]) ? { type: "log", line: value["line"] } : null;
|
|
55
55
|
case "repos": return role === "worker" && texts(value["repos"]) ? { type: "repos", repos: [...value["repos"]] } : null;
|
|
56
|
+
case "deferred": return role === "worker" && texts(value["repos"]) ? { type: "deferred", repos: [...value["repos"]] } : null;
|
|
56
57
|
case "failed": return (value["reason"] === "port-busy" || value["reason"] === "start") && text(value["detail"]) ? { type: "failed", reason: value["reason"], detail: value["detail"] } : null;
|
|
57
58
|
case "fatal": return text(value["detail"]) ? { type: "fatal", detail: value["detail"] } : null;
|
|
58
59
|
case "sign-in-link": return role === "console" && whole(value["id"]) && orNull(value["link"], (one) => nonEmpty(one, 2_048)) ? { type: "sign-in-link", id: value["id"], link: value["link"] } : null;
|
|
@@ -78,12 +79,15 @@ export function startMessage(value) {
|
|
|
78
79
|
return { ...base, role: "console", config: config };
|
|
79
80
|
}
|
|
80
81
|
if (value["role"] === "worker") {
|
|
81
|
-
const keys = ["runner", "token", "repos", "pinnedRepos", "projectRoots", "registryPath", "pool", "forMs", "publicUrl", "stopGraceMs", "verbose", "terminal", "leaseWaitMs"];
|
|
82
|
+
const keys = ["runner", "token", "repos", "pinnedRepos", "projectRoots", "registryPath", "pool", "forMs", "publicUrl", "stopGraceMs", "verbose", "terminal", "leaseWaitMs", "deferredRepos", "deferredGenerations", "repoLines"];
|
|
82
83
|
if (Object.keys(config).some(key => !keys.includes(key)))
|
|
83
84
|
return null;
|
|
84
85
|
if (!nonEmpty(config["runner"], 256) || !nonEmpty(config["token"], 1_024) || !texts(config["repos"]) || !texts(config["pinnedRepos"]) || !texts(config["projectRoots"]) ||
|
|
85
86
|
!nonEmpty(config["registryPath"], 4_096) || !nonEmpty(config["pool"], 4_096) || !orNull(config["forMs"], (one) => whole(one, 1)) ||
|
|
86
|
-
!orNull(config["publicUrl"], (one) => nonEmpty(one, 2_048)) || !whole(config["stopGraceMs"], 0) || typeof config["verbose"] !== "boolean" || typeof config["terminal"] !== "boolean" || !whole(config["leaseWaitMs"], 0)
|
|
87
|
+
!orNull(config["publicUrl"], (one) => nonEmpty(one, 2_048)) || !whole(config["stopGraceMs"], 0) || typeof config["verbose"] !== "boolean" || typeof config["terminal"] !== "boolean" || !whole(config["leaseWaitMs"], 0) ||
|
|
88
|
+
(config["deferredRepos"] !== undefined && !texts(config["deferredRepos"])) ||
|
|
89
|
+
(config["deferredGenerations"] !== undefined && !(isObject(config["deferredGenerations"]) && Object.values(config["deferredGenerations"]).every(one => whole(one, -1, Number.MAX_SAFE_INTEGER)))) ||
|
|
90
|
+
(config["repoLines"] !== undefined && !(isObject(config["repoLines"]) && Object.values(config["repoLines"]).every(one => texts(one)))))
|
|
87
91
|
return null;
|
|
88
92
|
return { ...base, role: "worker", config: config };
|
|
89
93
|
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "toolroll",
|
|
3
|
-
"version": "0.9.
|
|
3
|
+
"version": "0.9.83",
|
|
4
4
|
"description": "A control plane for unattended coding agents — queue tasks, walk away, come back to pull requests. Agents build in leased worktrees, never touch your default branch, and interrupt you only for decisions that need a human.",
|
|
5
5
|
"directories": {
|
|
6
6
|
"doc": "docs"
|