@abelxiaoxing/cadence 2.0.13 → 2.0.14
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 +141 -69
- package/THIRD_PARTY_NOTICES.md +1 -0
- package/docs/remote-access.md +13 -0
- package/docs/reuse-and-distribution.md +13 -2
- package/docs/service-management.md +31 -12
- package/licenses/pi-web-ui-reuse.md +2 -0
- package/package.json +5 -2
- package/src/conversation-context.mjs +147 -0
- package/src/conversation-resources.mjs +47 -0
- package/src/native-build.ts +1 -1
- package/src/native-client.ts +2 -2
- package/src/operator-cli.mjs +4 -7
- package/src/sdk-worker.mjs +41 -8
- package/src/service/contracts/workbench.ts +303 -0
- package/src/service/coordination.ts +1 -0
- package/src/service-cli.mjs +1227 -577
- package/src/service-client.ts +49 -0
- package/src/service-host.mjs +15768 -13011
- package/src/service-http-client.ts +50 -24
- package/src/service-wire.ts +26 -4
- package/src/task-store.ts +12 -11
- package/web/dist/assets/cadence.css +1 -1
- package/web/dist/assets/cadence.js +13 -7
- package/web/dist/manifest.json +7 -7
|
@@ -1,3 +1,7 @@
|
|
|
1
|
+
import type {
|
|
2
|
+
WorkbenchCommand,
|
|
3
|
+
WorkbenchRead,
|
|
4
|
+
} from "./service/contracts/workbench.ts";
|
|
1
5
|
import type { ServiceTransport } from "./service-client.ts";
|
|
2
6
|
import {
|
|
3
7
|
type CancelCommand,
|
|
@@ -63,6 +67,8 @@ export class HttpServiceTransport implements ServiceTransport {
|
|
|
63
67
|
constructor(
|
|
64
68
|
private readonly options: {
|
|
65
69
|
origin: string;
|
|
70
|
+
/** Authenticated same-origin project routing; never an arbitrary URL. */
|
|
71
|
+
project?: string;
|
|
66
72
|
build?: ClientBuild;
|
|
67
73
|
fetch?: typeof fetch;
|
|
68
74
|
/** Optional total bound for finite replies only, never streaming export. */
|
|
@@ -78,12 +84,26 @@ export class HttpServiceTransport implements ServiceTransport {
|
|
|
78
84
|
!/^http:\/\/(?:127\.0\.0\.1|\[::1\]):[1-9][0-9]*$/.test(options.origin))
|
|
79
85
|
)
|
|
80
86
|
throw new ContractError("invalid");
|
|
87
|
+
if (
|
|
88
|
+
options.project !== undefined &&
|
|
89
|
+
!/^[a-f0-9]{64}$/.test(options.project)
|
|
90
|
+
)
|
|
91
|
+
throw new ContractError("invalid");
|
|
81
92
|
this.origin = options.origin;
|
|
82
93
|
if (options.build) options.build = decodeClientBuild(options.build);
|
|
83
94
|
// Native browser fetch requires its Window receiver. Node/injected arrow
|
|
84
95
|
// fetchers tolerate the wrong `this`, so HTTP-only fixtures miss this bug.
|
|
85
96
|
this.fetcher = options.fetch ?? globalThis.fetch.bind(globalThis);
|
|
86
97
|
}
|
|
98
|
+
workbenchRead(
|
|
99
|
+
request: WorkbenchRead,
|
|
100
|
+
signal?: AbortSignal,
|
|
101
|
+
): Promise<unknown> {
|
|
102
|
+
return this.request("workbenchRead", request, signal);
|
|
103
|
+
}
|
|
104
|
+
workbenchCommand(request: WorkbenchCommand): Promise<unknown> {
|
|
105
|
+
return this.request("workbenchCommand", request);
|
|
106
|
+
}
|
|
87
107
|
private headers(write: boolean): Record<string, string> {
|
|
88
108
|
const headers: Record<string, string> = { "Cache-Control": "no-store" };
|
|
89
109
|
if (this.options.build)
|
|
@@ -109,20 +129,23 @@ export class HttpServiceTransport implements ServiceTransport {
|
|
|
109
129
|
write = wire.method === "POST";
|
|
110
130
|
// No automatic session/preflight refresh, changed command ID, mutation retry,
|
|
111
131
|
// credential query or redirect follow after an uncertain HTTP outcome.
|
|
112
|
-
const response = await this.fetcher(
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
132
|
+
const response = await this.fetcher(
|
|
133
|
+
`${this.origin}${this.options.project ? `/projects/${this.options.project}` : ""}${wire.path}`,
|
|
134
|
+
{
|
|
135
|
+
method: wire.method,
|
|
136
|
+
headers: this.headers(write),
|
|
137
|
+
credentials: "same-origin",
|
|
138
|
+
redirect: "error",
|
|
139
|
+
signal:
|
|
140
|
+
this.options.requestTimeoutMs === undefined
|
|
141
|
+
? signal
|
|
142
|
+
: AbortSignal.any([
|
|
143
|
+
...(signal ? [signal] : []),
|
|
144
|
+
AbortSignal.timeout(this.options.requestTimeoutMs),
|
|
145
|
+
]),
|
|
146
|
+
...(write ? { body: wire.body } : {}),
|
|
147
|
+
},
|
|
148
|
+
);
|
|
126
149
|
// Keep a known allocation even if its original response body is lost or
|
|
127
150
|
// malformed. Without a validated CSRF challenge detach may remain blocked;
|
|
128
151
|
// never silently forget that slot and acquire 16 replacement sessions.
|
|
@@ -273,14 +296,17 @@ export class HttpServiceTransport implements ServiceTransport {
|
|
|
273
296
|
): AsyncGenerator<unknown> {
|
|
274
297
|
const request = decodeExportRequest(input),
|
|
275
298
|
wire = wireRequest("export", request);
|
|
276
|
-
const response = await this.fetcher(
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
299
|
+
const response = await this.fetcher(
|
|
300
|
+
`${this.origin}${this.options.project ? `/projects/${this.options.project}` : ""}${wire.path}`,
|
|
301
|
+
{
|
|
302
|
+
method: wire.method,
|
|
303
|
+
headers: this.headers(wire.method === "POST"),
|
|
304
|
+
...(wire.body === undefined ? {} : { body: wire.body }),
|
|
305
|
+
credentials: "same-origin",
|
|
306
|
+
redirect: "error",
|
|
307
|
+
signal,
|
|
308
|
+
},
|
|
309
|
+
);
|
|
284
310
|
if (!response.ok || !response.body) {
|
|
285
311
|
await response.body?.cancel();
|
|
286
312
|
throw new ContractError("unavailable");
|
|
@@ -336,7 +362,7 @@ export class HttpServiceTransport implements ServiceTransport {
|
|
|
336
362
|
if (!this.attachment || !this.options.socket)
|
|
337
363
|
throw new ContractError("unavailable");
|
|
338
364
|
const socket = this.options.socket(
|
|
339
|
-
`${this.origin.replace(/^http/, "ws")}${WS_PATH}?attachment=${this.attachment}`,
|
|
365
|
+
`${this.origin.replace(/^http/, "ws")}${this.options.project ? `/projects/${this.options.project}` : ""}${WS_PATH}?attachment=${this.attachment}`,
|
|
340
366
|
);
|
|
341
367
|
let active = false,
|
|
342
368
|
closed = false,
|
|
@@ -401,7 +427,7 @@ export class HttpServiceTransport implements ServiceTransport {
|
|
|
401
427
|
return stop;
|
|
402
428
|
}
|
|
403
429
|
}
|
|
404
|
-
async function boundedText(
|
|
430
|
+
export async function boundedText(
|
|
405
431
|
response: Response,
|
|
406
432
|
maximum: number,
|
|
407
433
|
): Promise<string> {
|
package/src/service-wire.ts
CHANGED
|
@@ -1,4 +1,9 @@
|
|
|
1
1
|
import { closed, oneOf } from "./service/contracts/core.ts";
|
|
2
|
+
import {
|
|
3
|
+
decodeWorkbenchCommand,
|
|
4
|
+
decodeWorkbenchRead,
|
|
5
|
+
decodeWorkbenchResult,
|
|
6
|
+
} from "./service/contracts/workbench.ts";
|
|
2
7
|
/** Portable finite HTTP/WS route owner. Common DTO semantics stay in service-contract. */
|
|
3
8
|
import {
|
|
4
9
|
ContractError,
|
|
@@ -44,6 +49,8 @@ import {
|
|
|
44
49
|
|
|
45
50
|
export const HTTP_ROUTES = {
|
|
46
51
|
session: "/api/v1/session",
|
|
52
|
+
workbenchRead: "/api/v1/workbench/read",
|
|
53
|
+
workbenchCommand: "/api/v1/workbench/command",
|
|
47
54
|
current: "/api/v1/current",
|
|
48
55
|
currentChanges: "/api/v1/current-changes",
|
|
49
56
|
activeRuns: "/api/v1/active-runs",
|
|
@@ -102,7 +109,9 @@ export function httpOutputLimit(
|
|
|
102
109
|
operation === "activeRuns" ||
|
|
103
110
|
operation === "workspace" ||
|
|
104
111
|
operation === "models" ||
|
|
105
|
-
operation === "chooseModel"
|
|
112
|
+
operation === "chooseModel" ||
|
|
113
|
+
operation === "workbenchRead" ||
|
|
114
|
+
operation === "workbenchCommand"
|
|
106
115
|
)
|
|
107
116
|
return LIMITS.pageBytes;
|
|
108
117
|
if (operation === "graph") {
|
|
@@ -117,12 +126,21 @@ export function httpOutputLimit(
|
|
|
117
126
|
return LIMITS.controlBytes;
|
|
118
127
|
}
|
|
119
128
|
export function mutation(operation: HttpOperation): boolean {
|
|
120
|
-
return [
|
|
121
|
-
|
|
122
|
-
|
|
129
|
+
return [
|
|
130
|
+
"preflight",
|
|
131
|
+
"command",
|
|
132
|
+
"cancel",
|
|
133
|
+
"detach",
|
|
134
|
+
"chooseModel",
|
|
135
|
+
"workbenchCommand",
|
|
136
|
+
].includes(operation);
|
|
123
137
|
}
|
|
124
138
|
export function decodeHttpInput(operation: HttpOperation, value: unknown) {
|
|
125
139
|
switch (operation) {
|
|
140
|
+
case "workbenchRead":
|
|
141
|
+
return decodeWorkbenchRead(value);
|
|
142
|
+
case "workbenchCommand":
|
|
143
|
+
return decodeWorkbenchCommand(value);
|
|
126
144
|
case "session":
|
|
127
145
|
case "models":
|
|
128
146
|
case "activeRuns":
|
|
@@ -179,6 +197,9 @@ export function decodeHttpOutput(
|
|
|
179
197
|
if ((pageReaders as readonly string[]).includes(operation))
|
|
180
198
|
return decodeReply(value, decodeHistoryReadResult);
|
|
181
199
|
switch (operation) {
|
|
200
|
+
case "workbenchRead":
|
|
201
|
+
case "workbenchCommand":
|
|
202
|
+
return decodeReply(value, decodeWorkbenchResult);
|
|
182
203
|
case "current":
|
|
183
204
|
return decodeReply(value, decodeCurrentView);
|
|
184
205
|
case "currentChanges":
|
|
@@ -225,6 +246,7 @@ export function decodeHttpOutput(
|
|
|
225
246
|
export function readonlyBody(operation: HttpOperation): boolean {
|
|
226
247
|
const operations: readonly HttpOperation[] = [
|
|
227
248
|
...pageReaders,
|
|
249
|
+
"workbenchRead",
|
|
228
250
|
"current",
|
|
229
251
|
"currentChanges",
|
|
230
252
|
"checkObligations",
|
package/src/task-store.ts
CHANGED
|
@@ -251,7 +251,7 @@ export function savedRecordIdentity(record: TaskRecord): string {
|
|
|
251
251
|
return contentIdentity(recordText(record));
|
|
252
252
|
}
|
|
253
253
|
/** Resolve ambient aliases, including a missing tail, without creating anything. */
|
|
254
|
-
function canonicalAmbientRoot(path: string): string {
|
|
254
|
+
export function canonicalAmbientRoot(path: string): string {
|
|
255
255
|
const full = resolve(path);
|
|
256
256
|
try {
|
|
257
257
|
return realpathSync(full);
|
|
@@ -304,20 +304,21 @@ export function privateWorkspace(
|
|
|
304
304
|
canonicalAmbientRoot(requested),
|
|
305
305
|
);
|
|
306
306
|
}
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
* its ancestors) can never be a workspace; use ~/cadence instead of refusing.
|
|
310
|
-
* Observation only: the caller decides whether the landing may be created. */
|
|
307
|
+
/** A project must be explicit and outside private state. Never choose, create or
|
|
308
|
+
* reconstruct a default workspace. The workbench itself has no project cwd. */
|
|
311
309
|
export function effectiveWorkspace(
|
|
312
310
|
requested: string,
|
|
313
311
|
stateHome?: string,
|
|
314
|
-
|
|
312
|
+
_home?: string,
|
|
315
313
|
): { workspace: string; redirected: boolean } {
|
|
316
|
-
if (
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
|
|
314
|
+
if (
|
|
315
|
+
privateWorkspace(requested, stateHome) ||
|
|
316
|
+
stateHomeInside(resolveWorkspace(requested).root, stateHome)
|
|
317
|
+
)
|
|
318
|
+
throw new Error(
|
|
319
|
+
"Choose a project outside Cadence private state; no default workspace was created",
|
|
320
|
+
);
|
|
321
|
+
return { workspace: requested, redirected: false };
|
|
321
322
|
}
|
|
322
323
|
export interface CheckoutLock {
|
|
323
324
|
schema: "cadence-assistant-lock/1";
|