@gr8ful/spf 0.9.2 → 0.10.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/README.md +56 -0
- package/assets/defaults/spf.config.yaml +75 -0
- package/assets/skill/references/config.md +98 -4
- package/dist/chains/index.d.ts +2 -0
- package/dist/chains/index.js +4 -0
- package/dist/cli/commands/doctor.js +339 -2
- package/dist/cli/commands/fanout.d.ts +7 -14
- package/dist/cli/commands/fanout.js +45 -39
- package/dist/cli/commands/loop.d.ts +2 -0
- package/dist/cli/commands/loop.js +198 -0
- package/dist/cli/commands/run.js +14 -4
- package/dist/cli/commands/watch.d.ts +29 -1
- package/dist/cli/commands/watch.js +219 -64
- package/dist/cli/index.js +14 -0
- package/dist/core/agent_cc.d.ts +11 -0
- package/dist/core/agent_cc.js +25 -2
- package/dist/core/agent_flue.js +14 -5
- package/dist/core/agents.d.ts +61 -1
- package/dist/core/agents.js +363 -6
- package/dist/core/data_types.d.ts +316 -0
- package/dist/core/data_types.js +143 -0
- package/dist/core/loop.d.ts +230 -0
- package/dist/core/loop.js +290 -0
- package/dist/core/quality.d.ts +1 -2
- package/dist/core/sandbox.d.ts +236 -0
- package/dist/core/sandbox.js +655 -0
- package/dist/core/sandbox_cloudflare.d.ts +137 -0
- package/dist/core/sandbox_cloudflare.js +505 -0
- package/dist/core/sandbox_opensandbox.d.ts +59 -0
- package/dist/core/sandbox_opensandbox.js +484 -0
- package/dist/core/sandbox_sdk_types.d.ts +171 -0
- package/dist/core/sandbox_sdk_types.js +20 -0
- package/dist/core/watch.d.ts +56 -0
- package/dist/core/watch.js +354 -51
- package/dist/core/worktree_data.d.ts +1 -0
- package/dist/core/worktree_data.js +37 -0
- package/package.json +1 -1
|
@@ -0,0 +1,171 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Hand-written surface of `@alibaba-group/opensandbox` v0.1.11 (SPF #15
|
|
3
|
+
* design doc §4.1's "SDK dependency — no new entry in package.json").
|
|
4
|
+
*
|
|
5
|
+
* NOTHING in this file imports the real package — every shape below is
|
|
6
|
+
* copied from the spike's MEASURED API surface (see the scratchpad's
|
|
7
|
+
* `sandbox-spike.json`, `api_surface` field), not from the package's own
|
|
8
|
+
* `.d.ts` (which is never on disk in this tree: the SDK is a documented user
|
|
9
|
+
* install, §4.1, never a `package.json` dependency). `sandbox_opensandbox.ts`
|
|
10
|
+
* types `loadOpenSandboxSdk`'s return value against these interfaces so the
|
|
11
|
+
* whole tree typechecks with the SDK absent from `node_modules` — exactly
|
|
12
|
+
* the trade `CloudflareSandboxStub` makes deliberately
|
|
13
|
+
* (`dist/cloudflare/index.d.mts:6-12`: "A wrong object fails loudly on the
|
|
14
|
+
* first method call").
|
|
15
|
+
*
|
|
16
|
+
* A shape mismatch against a REAL installed SDK surfaces as a runtime error
|
|
17
|
+
* from the first call that touches the mismatched field, never as a compile
|
|
18
|
+
* error — there is nothing here for `tsc` to check it against.
|
|
19
|
+
*/
|
|
20
|
+
export interface OpenSandboxConnectionConfig {
|
|
21
|
+
domain: string;
|
|
22
|
+
protocol?: "http" | "https";
|
|
23
|
+
apiKey?: string;
|
|
24
|
+
/** The SDK's CLIENT-SIDE HTTP timeout for control-plane calls. NOT a bound on a streamed `commands.run`. */
|
|
25
|
+
requestTimeoutSeconds?: number;
|
|
26
|
+
headers?: Record<string, string>;
|
|
27
|
+
useServerProxy?: boolean;
|
|
28
|
+
}
|
|
29
|
+
export interface OpenSandboxNetworkPolicy {
|
|
30
|
+
defaultAction: "allow" | "deny";
|
|
31
|
+
egress?: Array<{
|
|
32
|
+
action: "allow" | "deny";
|
|
33
|
+
target: string;
|
|
34
|
+
}>;
|
|
35
|
+
}
|
|
36
|
+
export interface OpenSandboxCreateOptions {
|
|
37
|
+
connectionConfig: OpenSandboxConnectionConfig;
|
|
38
|
+
image: string;
|
|
39
|
+
entrypoint?: string;
|
|
40
|
+
/** Provider-side lease expiry, in seconds — `sandbox.renew()` extends it while a lease is live. */
|
|
41
|
+
timeoutSeconds?: number;
|
|
42
|
+
env?: Record<string, string>;
|
|
43
|
+
resource?: unknown;
|
|
44
|
+
metadata?: Record<string, string>;
|
|
45
|
+
networkPolicy?: OpenSandboxNetworkPolicy;
|
|
46
|
+
credentialProxy?: unknown;
|
|
47
|
+
extensions?: unknown;
|
|
48
|
+
skipHealthCheck?: boolean;
|
|
49
|
+
healthCheck?: unknown;
|
|
50
|
+
/** Readiness bound — distinct from `requestTimeoutSeconds`. The spike measured a 30-90s cold egress-sidecar start against exactly this knob. */
|
|
51
|
+
readyTimeoutSeconds?: number;
|
|
52
|
+
}
|
|
53
|
+
export interface OpenSandboxCommandOptions {
|
|
54
|
+
/** Maps to the API's `cwd` — the SDK's own field name is `workingDirectory`, not `cwd` (confirmed against the installed SDK's `.d.ts`). */
|
|
55
|
+
workingDirectory?: string;
|
|
56
|
+
/** The SDK's own field name is `envs`, not `env`. */
|
|
57
|
+
envs?: Record<string, string>;
|
|
58
|
+
/**
|
|
59
|
+
* O-1 (open question, design §12): the spike documented `commands.run`'s
|
|
60
|
+
* signature but never measured whether it accepts a per-command deadline.
|
|
61
|
+
* Forwarded best-effort, seconds, rounded UP per `sandbox-api.md:225` —
|
|
62
|
+
* the adapter's OWN host-side timer (§4.1 point 3) is what actually
|
|
63
|
+
* enforces the deadline regardless of whether the SDK honors this field.
|
|
64
|
+
*/
|
|
65
|
+
timeoutSeconds?: number;
|
|
66
|
+
}
|
|
67
|
+
export interface OpenSandboxLogChunk {
|
|
68
|
+
text: string;
|
|
69
|
+
}
|
|
70
|
+
export interface OpenSandboxCommandResult {
|
|
71
|
+
logs: {
|
|
72
|
+
stdout: OpenSandboxLogChunk[];
|
|
73
|
+
stderr: OpenSandboxLogChunk[];
|
|
74
|
+
};
|
|
75
|
+
exitCode: number;
|
|
76
|
+
executionTimeMs: number;
|
|
77
|
+
}
|
|
78
|
+
export interface OpenSandboxExecutionHandlers {
|
|
79
|
+
onStdout?: (chunk: OpenSandboxLogChunk) => void;
|
|
80
|
+
onStderr?: (chunk: OpenSandboxLogChunk) => void;
|
|
81
|
+
onExecutionComplete?: (result: OpenSandboxCommandResult) => void;
|
|
82
|
+
}
|
|
83
|
+
export interface OpenSandboxCommandsApi {
|
|
84
|
+
run(command: string, options?: OpenSandboxCommandOptions, handlers?: OpenSandboxExecutionHandlers): Promise<OpenSandboxCommandResult>;
|
|
85
|
+
}
|
|
86
|
+
export interface OpenSandboxDirEntry {
|
|
87
|
+
path: string;
|
|
88
|
+
mode?: number;
|
|
89
|
+
}
|
|
90
|
+
export interface OpenSandboxFileEntry {
|
|
91
|
+
path: string;
|
|
92
|
+
data: string | Uint8Array;
|
|
93
|
+
mode?: number;
|
|
94
|
+
}
|
|
95
|
+
export interface OpenSandboxFilesApi {
|
|
96
|
+
createDirectories(dirs: OpenSandboxDirEntry[]): Promise<void>;
|
|
97
|
+
writeFiles(files: OpenSandboxFileEntry[]): Promise<void>;
|
|
98
|
+
readFile(path: string): Promise<string>;
|
|
99
|
+
search(options: {
|
|
100
|
+
path: string;
|
|
101
|
+
pattern: string;
|
|
102
|
+
}): Promise<unknown>;
|
|
103
|
+
deleteDirectories(paths: string[]): Promise<void>;
|
|
104
|
+
}
|
|
105
|
+
export interface OpenSandboxInfo {
|
|
106
|
+
status: {
|
|
107
|
+
state: string;
|
|
108
|
+
};
|
|
109
|
+
createdAt: string;
|
|
110
|
+
expiresAt: string;
|
|
111
|
+
}
|
|
112
|
+
export interface OpenSandboxEgressRule {
|
|
113
|
+
action: "allow" | "deny";
|
|
114
|
+
target: string;
|
|
115
|
+
}
|
|
116
|
+
export interface OpenSandboxEgressPolicy {
|
|
117
|
+
defaultAction: "allow" | "deny";
|
|
118
|
+
egress: OpenSandboxEgressRule[];
|
|
119
|
+
}
|
|
120
|
+
export interface OpenSandboxInstance {
|
|
121
|
+
readonly id: string;
|
|
122
|
+
commands: OpenSandboxCommandsApi;
|
|
123
|
+
files: OpenSandboxFilesApi;
|
|
124
|
+
getInfo(): Promise<OpenSandboxInfo>;
|
|
125
|
+
pause(): Promise<void>;
|
|
126
|
+
/** Resumes returns a FRESH connected instance — not `this`, per the spike. */
|
|
127
|
+
resume(): Promise<OpenSandboxInstance>;
|
|
128
|
+
renew(seconds: number): Promise<void>;
|
|
129
|
+
/** Terminates the remote sandbox + sidecar. Does NOT release the local HTTP agent — see `close()`. */
|
|
130
|
+
kill(): Promise<void>;
|
|
131
|
+
/** Releases the Node undici keep-alive agent. Separate from `kill()` — BOTH are required teardown calls (spike caveat). */
|
|
132
|
+
close(): Promise<void>;
|
|
133
|
+
getEndpoint(port: number): Promise<{
|
|
134
|
+
endpoint: string;
|
|
135
|
+
}>;
|
|
136
|
+
getEndpointUrl(port: number): Promise<string>;
|
|
137
|
+
/** Talks directly to the egress sidecar's own REST surface on sandbox port 18080 — NOT proxied through the control plane. */
|
|
138
|
+
getEgressPolicy(): Promise<OpenSandboxEgressPolicy>;
|
|
139
|
+
patchEgressRules(rules: OpenSandboxEgressRule[]): Promise<void>;
|
|
140
|
+
}
|
|
141
|
+
export interface OpenSandboxCtor {
|
|
142
|
+
create(options: OpenSandboxCreateOptions): Promise<OpenSandboxInstance>;
|
|
143
|
+
}
|
|
144
|
+
export interface OpenSandboxManagerListItem {
|
|
145
|
+
id: string;
|
|
146
|
+
metadata?: Record<string, string>;
|
|
147
|
+
status?: {
|
|
148
|
+
state: string;
|
|
149
|
+
};
|
|
150
|
+
}
|
|
151
|
+
export interface OpenSandboxManagerListResult {
|
|
152
|
+
items: OpenSandboxManagerListItem[];
|
|
153
|
+
pagination?: unknown;
|
|
154
|
+
}
|
|
155
|
+
export interface OpenSandboxManagerInstance {
|
|
156
|
+
listSandboxInfos(options?: {
|
|
157
|
+
states?: string[];
|
|
158
|
+
pageSize?: number;
|
|
159
|
+
}): Promise<OpenSandboxManagerListResult>;
|
|
160
|
+
close(): Promise<void>;
|
|
161
|
+
}
|
|
162
|
+
export interface OpenSandboxManagerCtor {
|
|
163
|
+
create(options: {
|
|
164
|
+
connectionConfig: OpenSandboxConnectionConfig;
|
|
165
|
+
}): Promise<OpenSandboxManagerInstance>;
|
|
166
|
+
}
|
|
167
|
+
/** The module namespace shape `import(OPENSANDBOX_SPECIFIER)` resolves to. */
|
|
168
|
+
export interface OpenSandboxModuleShape {
|
|
169
|
+
Sandbox: OpenSandboxCtor;
|
|
170
|
+
SandboxManager: OpenSandboxManagerCtor;
|
|
171
|
+
}
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Hand-written surface of `@alibaba-group/opensandbox` v0.1.11 (SPF #15
|
|
3
|
+
* design doc §4.1's "SDK dependency — no new entry in package.json").
|
|
4
|
+
*
|
|
5
|
+
* NOTHING in this file imports the real package — every shape below is
|
|
6
|
+
* copied from the spike's MEASURED API surface (see the scratchpad's
|
|
7
|
+
* `sandbox-spike.json`, `api_surface` field), not from the package's own
|
|
8
|
+
* `.d.ts` (which is never on disk in this tree: the SDK is a documented user
|
|
9
|
+
* install, §4.1, never a `package.json` dependency). `sandbox_opensandbox.ts`
|
|
10
|
+
* types `loadOpenSandboxSdk`'s return value against these interfaces so the
|
|
11
|
+
* whole tree typechecks with the SDK absent from `node_modules` — exactly
|
|
12
|
+
* the trade `CloudflareSandboxStub` makes deliberately
|
|
13
|
+
* (`dist/cloudflare/index.d.mts:6-12`: "A wrong object fails loudly on the
|
|
14
|
+
* first method call").
|
|
15
|
+
*
|
|
16
|
+
* A shape mismatch against a REAL installed SDK surfaces as a runtime error
|
|
17
|
+
* from the first call that touches the mismatched field, never as a compile
|
|
18
|
+
* error — there is nothing here for `tsc` to check it against.
|
|
19
|
+
*/
|
|
20
|
+
export {};
|
package/dist/core/watch.d.ts
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import type { GitHandle } from "./git_helper.ts";
|
|
2
|
+
import { type AttemptDispatch, type AttemptMetrics } from "./fanout.ts";
|
|
2
3
|
import type { CodeHostProvider, Issue, IssueComment, IssueProvider, WatchMarker, WatchState } from "./issues/provider.ts";
|
|
3
4
|
import type { NotifyEvent } from "./notify/channel.ts";
|
|
4
5
|
import { type RefinedPriority } from "./data_types.ts";
|
|
@@ -52,6 +53,50 @@ export interface RefineRunResult {
|
|
|
52
53
|
/** What the refiner is asking, read back from its own side-channel file — see `cli/commands/watch.ts`'s `runRefine`. Empty when `!accepted` or when the run published a tree instead of escalating; `gates.refinementWellFormed` guarantees `created` and `questions` are never both non-empty. */
|
|
53
54
|
questions: RefinedQuestionRef[];
|
|
54
55
|
}
|
|
56
|
+
/**
|
|
57
|
+
* The fan-out lane's injected trio + the two values `WatchDeps` cannot derive.
|
|
58
|
+
* Exported as a NAMED type (not an inline object literal) because
|
|
59
|
+
* `cli/commands/watch.ts`'s `makeWatchFanoutDispatch` factory returns it and
|
|
60
|
+
* `src/test/watch_fanout_sandbox_wiring.test.ts` names it — an inline literal
|
|
61
|
+
* would have to be duplicated in three places.
|
|
62
|
+
*/
|
|
63
|
+
export interface WatchFanoutDeps {
|
|
64
|
+
/** > 1. `cli/commands/watch.ts` passes `undefined` for this whole object when `watch.fanout.n` is 1. */
|
|
65
|
+
n: number;
|
|
66
|
+
/** Attempts IN FLIGHT within one issue — NOT `WatchDeps.concurrency`, which counts issues. Does NOT bound worktrees on disk (see the module's fan-out doc). */
|
|
67
|
+
concurrency: number;
|
|
68
|
+
/** The MAIN repo root. `GitHandle` has no accessor for it — `cli/commands/watch.ts` passes its anchor's `repo_root`, the same value `deps.git` is bound to. */
|
|
69
|
+
repoRoot: string;
|
|
70
|
+
/** One attempt's chain, in that attempt's own worktree — the same ctx `runChain` builds, per attempt. */
|
|
71
|
+
runAttempt: (dispatch: AttemptDispatch) => Promise<number>;
|
|
72
|
+
/** Gate/usage rows for one attempt's adw_id, from the SHARED db. Must not throw. */
|
|
73
|
+
readMetrics: (adwId: string) => AttemptMetrics;
|
|
74
|
+
/**
|
|
75
|
+
* True when NOT ONE of these adw_ids has a session row yet — the same db,
|
|
76
|
+
* the same predicate and the same reasoning as `spf fanout`'s reuse
|
|
77
|
+
* preflight (`cli/commands/fanout.ts`, `preflight.session(id) !== null`).
|
|
78
|
+
* This is what advances the fan-out lane's base-adw_id salt on EVERY claim
|
|
79
|
+
* (see `runIssueFanout`'s salt probe) — the sessions the previous claim's
|
|
80
|
+
* attempts left behind are the only thing that reliably changes on every
|
|
81
|
+
* re-claim, unlike `WatchMarker.attempt`, which only advances on the
|
|
82
|
+
* orphan-retry path.
|
|
83
|
+
*
|
|
84
|
+
* Must not throw, and its safe direction is FALSE: a db it cannot read
|
|
85
|
+
* reports "taken", which burns an id and keeps the selection basis clean,
|
|
86
|
+
* rather than reporting "free" and letting a previous run's rows decide
|
|
87
|
+
* this run's winner.
|
|
88
|
+
*/
|
|
89
|
+
adwIdsFree: (adwIds: string[]) => boolean;
|
|
90
|
+
/** The winner's review posture, read back post-hoc — the same two fields `runChain` returns, keyed on a WINNER instead of the sole attempt. */
|
|
91
|
+
reviewFor: (opts: {
|
|
92
|
+
cwd: string;
|
|
93
|
+
adwId: string;
|
|
94
|
+
chainOptions: Record<string, string>;
|
|
95
|
+
}) => {
|
|
96
|
+
reviewRequired: boolean;
|
|
97
|
+
reviewSummary?: string;
|
|
98
|
+
};
|
|
99
|
+
}
|
|
55
100
|
export interface WatchDeps {
|
|
56
101
|
provider: IssueProvider;
|
|
57
102
|
codeHost: CodeHostProvider;
|
|
@@ -133,6 +178,17 @@ export interface WatchDeps {
|
|
|
133
178
|
* just be noise in a channel.
|
|
134
179
|
*/
|
|
135
180
|
notify: (event: NotifyEvent) => void;
|
|
181
|
+
/**
|
|
182
|
+
* Best-of-N per claimed issue (`watch.fanout`). `undefined` — the default,
|
|
183
|
+
* and what every existing test constructs — means single dispatch:
|
|
184
|
+
* `runIssue` takes the path it always has.
|
|
185
|
+
*
|
|
186
|
+
* ONE OPTIONAL OBJECT, not several optional fields: everything the fan-out
|
|
187
|
+
* lane needs is required WHEN THE LANE IS ON and meaningless when it is
|
|
188
|
+
* off, so the optionality belongs at the lane, not at each field. There is
|
|
189
|
+
* no "optional but secretly required" member to get wrong.
|
|
190
|
+
*/
|
|
191
|
+
fanout?: WatchFanoutDeps;
|
|
136
192
|
}
|
|
137
193
|
export interface WatchRunState {
|
|
138
194
|
inflight: Set<string>;
|