@xfey/tutti 0.1.119 → 0.1.121
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/approvals/decision-command.d.ts +2 -2
- package/dist/collaboration-state/clarification-snapshot.d.ts +9 -9
- package/dist/collaboration-state/task-compile-context.d.ts +1 -1
- package/dist/control-plane/workflows/openai.d.ts +3 -4
- package/dist/control-plane/workflows/openai.js +3 -2
- package/dist/providers/openai/app-server/invocation-coordinator.d.ts +1 -0
- package/dist/providers/openai/app-server/invocation-coordinator.js +4 -1
- package/dist/providers/openai/app-server/invocation-deadline.d.ts +3 -0
- package/dist/providers/openai/app-server/invocation-deadline.js +15 -0
- package/dist/providers/openai/app-server/read-only-procedure.d.ts +4 -2
- package/dist/providers/openai/app-server/read-only-procedure.js +16 -0
- package/dist/providers/openai/app-server/workspace-write-run.d.ts +4 -2
- package/dist/providers/openai/app-server/workspace-write-run.js +16 -0
- package/dist/providers/openai/chat-assistant.d.ts +3 -4
- package/dist/providers/openai/chat-assistant.js +2 -1
- package/dist/providers/openai/execution-access.d.ts +17 -0
- package/dist/providers/openai/execution-access.js +13 -0
- package/dist/providers/openai/sdk-procedure-runner.d.ts +2 -2
- package/dist/providers/openai/sdk-procedure-runner.js +17 -0
- package/dist/run-pipeline/artifact-applicability.d.ts +3 -4
- package/dist/run-pipeline/artifact-applicability.js +2 -1
- package/dist/run-pipeline/openai.d.ts +3 -4
- package/dist/run-pipeline/openai.js +3 -2
- package/dist/run-pipeline/task-run-invocation.js +6 -12
- package/dist/server-shell/cli/cli.js +3 -0
- package/dist/server-shell/cli/host-lifecycle.js +1 -0
- package/dist/server-shell/cli/host-server-runtime.d.ts +1 -0
- package/dist/server-shell/cli/host-server-runtime.js +83 -34
- package/dist/server-shell/cli/launch-command.js +1 -1
- package/dist/server-shell/cli/launch.d.ts +7 -3
- package/dist/server-shell/cli/launch.js +23 -4
- package/dist/server-shell/cli/machine-local.d.ts +9 -0
- package/dist/server-shell/cli/machine-local.js +60 -6
- package/dist/server-shell/cli/project-resolver.d.ts +2 -2
- package/dist/server-shell/cli/project-resolver.js +6 -3
- package/dist/server-shell/cli/relay-registration.js +38 -1
- package/dist/server-shell/http/routes/local-control.d.ts +1 -1
- package/dist/server-shell/http/routes/local-control.js +2 -46
- package/dist/server-shell/http/routes/project-api/openapi-workspace-routes.d.ts +45 -137
- package/dist/server-shell/http/routes/project-api/openapi.d.ts +45 -137
- package/dist/server-shell/http/routes/project-api/schemas.d.ts +24 -136
- package/dist/server-shell/http/routes/project-api/schemas.js +4 -58
- package/dist/server-shell/http/routes/project-api/types.d.ts +3 -0
- package/dist/server-shell/local-console/managed-project-routes.d.ts +9 -0
- package/dist/server-shell/local-console/managed-project-routes.js +120 -0
- package/dist/server-shell/local-console/managed-project-service.d.ts +141 -0
- package/dist/server-shell/local-console/managed-project-service.js +158 -0
- package/dist/server-shell/local-console/operation-coordinator.d.ts +1 -1
- package/dist/server-shell/local-console/package-update-hosts.js +2 -1
- package/dist/server-shell/local-console/project-service.d.ts +4 -1
- package/dist/server-shell/local-console/project-service.js +62 -14
- package/dist/server-shell/local-console/server.js +22 -2
- package/dist/server-shell/managed-access/execution-binding.d.ts +9 -0
- package/dist/server-shell/managed-access/execution-binding.js +22 -0
- package/dist/server-shell/managed-access/existing-project.d.ts +15 -0
- package/dist/server-shell/managed-access/existing-project.js +41 -0
- package/dist/server-shell/managed-access/legacy-records.d.ts +2 -0
- package/dist/server-shell/managed-access/legacy-records.js +24 -0
- package/dist/server-shell/managed-access/model-control-client.d.ts +65 -0
- package/dist/server-shell/managed-access/model-control-client.js +112 -0
- package/dist/server-shell/managed-access/private-store.d.ts +19 -0
- package/dist/server-shell/managed-access/private-store.js +275 -0
- package/dist/server-shell/managed-access/project-provider.d.ts +18 -0
- package/dist/server-shell/managed-access/project-provider.js +30 -0
- package/dist/server-shell/managed-access/records.d.ts +103 -0
- package/dist/server-shell/managed-access/records.js +74 -0
- package/dist/server-shell/managed-access/runners.d.ts +11 -0
- package/dist/server-shell/managed-access/runners.js +103 -0
- package/dist/server-shell/managed-access/runtime-records.d.ts +106 -0
- package/dist/server-shell/managed-access/runtime-records.js +65 -0
- package/dist/server-shell/managed-access/runtime.d.ts +48 -0
- package/dist/server-shell/managed-access/runtime.js +300 -0
- package/dist/server-shell/managed-access/state.d.ts +53 -0
- package/dist/server-shell/managed-access/state.js +292 -0
- package/dist/server-shell/managed-access/transport.d.ts +45 -0
- package/dist/server-shell/managed-access/transport.js +39 -0
- package/dist/server-shell/managed-access/workspace.d.ts +10 -0
- package/dist/server-shell/managed-access/workspace.js +69 -0
- package/dist/server-shell/smoke/e2e-client.d.ts +8 -3
- package/dist/server-shell/smoke/e2e-client.js +11 -11
- package/node_modules/@tutti/relay-client/dist/host-control.d.ts +4 -1
- package/node_modules/@tutti/relay-client/dist/host-control.js +26 -3
- package/node_modules/@tutti/relay-client/dist/index.d.ts +1 -0
- package/node_modules/@tutti/relay-client/dist/index.js +1 -0
- package/node_modules/@tutti/relay-client/dist/project-authority.d.ts +70 -0
- package/node_modules/@tutti/relay-client/dist/project-authority.js +139 -0
- package/node_modules/@tutti/shared/dist/ids/index.d.ts +7 -0
- package/node_modules/@tutti/shared/dist/ids/index.js +7 -0
- package/node_modules/@tutti/shared/dist/schemas/api/index.d.ts +1 -0
- package/node_modules/@tutti/shared/dist/schemas/api/index.js +1 -0
- package/node_modules/@tutti/shared/dist/schemas/api/managed-account.d.ts +34 -0
- package/node_modules/@tutti/shared/dist/schemas/api/managed-account.js +40 -0
- package/node_modules/@tutti/shared/dist/schemas/api/provider-config.d.ts +7 -4
- package/node_modules/@tutti/shared/dist/schemas/api/provider-config.js +5 -0
- package/node_modules/@tutti/shared/dist/schemas/api/runtime-events.d.ts +3 -0
- package/node_modules/@tutti/shared/dist/schemas/internal/creation-validation.d.ts +26 -0
- package/node_modules/@tutti/shared/dist/schemas/internal/creation-validation.js +40 -0
- package/node_modules/@tutti/shared/dist/schemas/internal/index.d.ts +5 -0
- package/node_modules/@tutti/shared/dist/schemas/internal/index.js +5 -0
- package/node_modules/@tutti/shared/dist/schemas/internal/managed-account.d.ts +12 -0
- package/node_modules/@tutti/shared/dist/schemas/internal/managed-account.js +13 -0
- package/node_modules/@tutti/shared/dist/schemas/internal/managed-models.d.ts +17 -0
- package/node_modules/@tutti/shared/dist/schemas/internal/managed-models.js +17 -0
- package/node_modules/@tutti/shared/dist/schemas/internal/managed-runtime.d.ts +113 -0
- package/node_modules/@tutti/shared/dist/schemas/internal/managed-runtime.js +84 -0
- package/node_modules/@tutti/shared/dist/schemas/internal/project-authority.d.ts +114 -0
- package/node_modules/@tutti/shared/dist/schemas/internal/project-authority.js +102 -0
- package/package.json +1 -1
- package/web/assets/{homepage-motion-scene-B6lCCHBq.js → homepage-motion-scene-WfLyzcCG.js} +1 -1
- package/web/assets/{index-DsYi7AhN.css → index-ATsYiBZs.css} +1 -1
- package/web/assets/index-o9YCtzEd.js +70 -0
- package/web/index.html +2 -2
- package/web/assets/index-DepsYMLl.js +0 -70
|
@@ -3,7 +3,9 @@ import { resolve } from "node:path";
|
|
|
3
3
|
import { readRelayProjectMembershipState } from "@tutti/relay-client";
|
|
4
4
|
import { createProjectSurfaceProfile, } from "@tutti/shared/domain";
|
|
5
5
|
import { relayProjectRouteSegment } from "@tutti/shared/ids";
|
|
6
|
-
import {
|
|
6
|
+
import { hasProjectModelConfiguration, readProjectModelAccess, } from "../managed-access/project-provider.js";
|
|
7
|
+
import { materializeManagedWorkspace } from "../managed-access/workspace.js";
|
|
8
|
+
import { configureProjectOpenAiProvider, discoverOpenAiModels, isOpenAiApiKey, validateOpenAiCredential, } from "../../providers/openai/index.js";
|
|
7
9
|
import { formatCliErrorReason, LaunchError } from "../cli/errors.js";
|
|
8
10
|
import { createRuntimeEndpointProbe, waitForHostShutdown, } from "../cli/host-runtime-endpoint.js";
|
|
9
11
|
import { prepareLaunchProject, resolveLaunchLocalContext, resolveRelayUrl } from "../cli/launch.js";
|
|
@@ -207,18 +209,27 @@ export class LocalConsoleProjectService {
|
|
|
207
209
|
// Damaged legacy metadata remains visible without an invented creation date.
|
|
208
210
|
}
|
|
209
211
|
}
|
|
210
|
-
const provider =
|
|
212
|
+
const provider = readProjectModelAccess({
|
|
211
213
|
tuttiHome: this.#tuttiHome,
|
|
212
214
|
projectId: row.project_id,
|
|
215
|
+
relayUrl: binding?.relay_url ?? resolveRelayUrl(operation.env),
|
|
213
216
|
});
|
|
214
|
-
const providerModel = provider.status === "not_configured"
|
|
217
|
+
const providerModel = provider.status === "not_configured"
|
|
218
|
+
? undefined
|
|
219
|
+
: provider.status === "managed"
|
|
220
|
+
? "Tutti"
|
|
221
|
+
: provider.default_model;
|
|
215
222
|
const activityAt = readProjectActivityAt(this.#tuttiHome, row, binding);
|
|
216
|
-
return
|
|
217
|
-
...(
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
223
|
+
return {
|
|
224
|
+
...projectFromRuntimeRow(row, provider.status, providerModel, {
|
|
225
|
+
...(activityAt === undefined ? {} : { activityAt }),
|
|
226
|
+
...(createdAt === undefined ? {} : { createdAt }),
|
|
227
|
+
relayUrl: binding?.relay_url ??
|
|
228
|
+
relayUrlFromJoinUrl(row.join_url) ??
|
|
229
|
+
resolveRelayUrl(operation.env),
|
|
230
|
+
surfaceProfile: binding?.surface_profile ?? createProjectSurfaceProfile(),
|
|
231
|
+
}),
|
|
232
|
+
};
|
|
222
233
|
});
|
|
223
234
|
}
|
|
224
235
|
async #readProject(projectId, context) {
|
|
@@ -325,12 +336,14 @@ export class LocalConsoleProjectService {
|
|
|
325
336
|
#requireStoredLaunchProvider(workspacePath, operation) {
|
|
326
337
|
const context = resolveLaunchLocalContext({ workspacePath, ...operation });
|
|
327
338
|
const provider = context.existing_identity.kind === "present"
|
|
328
|
-
?
|
|
339
|
+
? readProjectModelAccess({
|
|
329
340
|
tuttiHome: context.tutti_home,
|
|
330
341
|
projectId: context.existing_identity.project_id,
|
|
342
|
+
relayUrl: readMachineProjectBinding(context.tutti_home, context.existing_identity.project_id)
|
|
343
|
+
?.relay_url ?? resolveRelayUrl(operation.env),
|
|
331
344
|
})
|
|
332
345
|
: { status: "not_configured" };
|
|
333
|
-
if (provider
|
|
346
|
+
if (!hasProjectModelConfiguration(provider)) {
|
|
334
347
|
throw new LocalConsoleProjectError("provider_configuration_required", "This project does not have a valid saved Provider. Configure it in the launch form.");
|
|
335
348
|
}
|
|
336
349
|
}
|
|
@@ -359,6 +372,18 @@ export class LocalConsoleProjectService {
|
|
|
359
372
|
}
|
|
360
373
|
async #launchProject(input, context, accountHandoff) {
|
|
361
374
|
const operation = this.#operationOptions(context);
|
|
375
|
+
if (input.provider !== undefined) {
|
|
376
|
+
const local = resolveLaunchLocalContext({ workspacePath: input.workspacePath, ...operation });
|
|
377
|
+
if (local.existing_identity.kind === "present" &&
|
|
378
|
+
readProjectModelAccess({
|
|
379
|
+
tuttiHome: local.tutti_home,
|
|
380
|
+
projectId: local.existing_identity.project_id,
|
|
381
|
+
relayUrl: readMachineProjectBinding(local.tutti_home, local.existing_identity.project_id)
|
|
382
|
+
?.relay_url ?? resolveRelayUrl(operation.env),
|
|
383
|
+
}).status === "managed") {
|
|
384
|
+
throw new LocalConsoleProjectError("managed_model_fixed", "Managed projects cannot be changed through BYOK setup.", 409);
|
|
385
|
+
}
|
|
386
|
+
}
|
|
362
387
|
if (input.provider === undefined) {
|
|
363
388
|
try {
|
|
364
389
|
this.#requireStoredLaunchProvider(input.workspacePath, operation);
|
|
@@ -388,6 +413,9 @@ export class LocalConsoleProjectService {
|
|
|
388
413
|
catch (error) {
|
|
389
414
|
throw this.#normalizeError(error);
|
|
390
415
|
}
|
|
416
|
+
if (provider !== undefined && preparation.provider_status === "managed") {
|
|
417
|
+
throw new LocalConsoleProjectError("managed_model_fixed", "Managed projects cannot be changed through BYOK setup.", 409);
|
|
418
|
+
}
|
|
391
419
|
if (provider !== undefined) {
|
|
392
420
|
const configured = await configureProjectOpenAiProvider({
|
|
393
421
|
tuttiHome: preparation.tutti_home,
|
|
@@ -401,10 +429,11 @@ export class LocalConsoleProjectService {
|
|
|
401
429
|
throw new LocalConsoleProjectError(configured.reason, localConsoleProviderValidationMessage(configured));
|
|
402
430
|
}
|
|
403
431
|
}
|
|
404
|
-
else if (preparation.
|
|
432
|
+
else if (!hasProjectModelConfiguration(preparation.provider_config)) {
|
|
405
433
|
throw new LocalConsoleProjectError("provider_configuration_required", "This project does not have a valid saved Provider. Configure it in the launch form.");
|
|
406
434
|
}
|
|
407
435
|
if (preparation.machine_local.binding.surface_profile.kind === "feishu" &&
|
|
436
|
+
preparation.provider_status !== "managed" &&
|
|
408
437
|
accountHandoff !== undefined) {
|
|
409
438
|
writeFeishuOwnerAccountHandoff({
|
|
410
439
|
tuttiHome: preparation.tutti_home,
|
|
@@ -414,12 +443,31 @@ export class LocalConsoleProjectService {
|
|
|
414
443
|
now: this.#now(),
|
|
415
444
|
});
|
|
416
445
|
}
|
|
446
|
+
return this.#finishLaunch(preparation, context, operation.env);
|
|
447
|
+
}
|
|
448
|
+
// Called only by the native authority service while holding the Console operation
|
|
449
|
+
// guard. It accepts a private, verified journal, never a browser-provided receipt.
|
|
450
|
+
async launchAuthorizedProject(state, requestRef, context) {
|
|
451
|
+
const operation = this.#operationOptions(context);
|
|
452
|
+
const preparation = await materializeManagedWorkspace({
|
|
453
|
+
state,
|
|
454
|
+
requestRef,
|
|
455
|
+
tuttiHome: this.#tuttiHome,
|
|
456
|
+
environment: operation.env,
|
|
457
|
+
now: this.#now,
|
|
458
|
+
});
|
|
459
|
+
return this.#finishLaunch(preparation, context, {
|
|
460
|
+
...operation.env,
|
|
461
|
+
TUTTI_RELAY_URL: state.origin,
|
|
462
|
+
});
|
|
463
|
+
}
|
|
464
|
+
async #finishLaunch(preparation, context, environment) {
|
|
417
465
|
try {
|
|
418
466
|
const ready = await this.#startManagedHost({
|
|
419
467
|
workspaceRoot: preparation.workspace_root,
|
|
420
468
|
tuttiHome: preparation.tutti_home,
|
|
421
469
|
projectId: preparation.project_id,
|
|
422
|
-
env:
|
|
470
|
+
env: environment,
|
|
423
471
|
});
|
|
424
472
|
if (preparation.machine_local.binding.surface_profile.kind === "standalone" &&
|
|
425
473
|
ready.join_url === undefined) {
|
|
@@ -480,7 +528,7 @@ export class LocalConsoleProjectService {
|
|
|
480
528
|
}
|
|
481
529
|
if (!managed.workspace_exists ||
|
|
482
530
|
managed.runtime_endpoint === null ||
|
|
483
|
-
managed.provider_config
|
|
531
|
+
!hasProjectModelConfiguration(managed.provider_config)) {
|
|
484
532
|
return { project: current, hostUpdate: "deferred_unavailable" };
|
|
485
533
|
}
|
|
486
534
|
let shutdownDisposition;
|
|
@@ -1,7 +1,10 @@
|
|
|
1
1
|
import { timingSafeEqual } from "node:crypto";
|
|
2
|
+
import { LocalConsoleManagedProjectService } from "./managed-project-service.js";
|
|
3
|
+
import { registerLocalConsoleManagedProjectRoutes } from "./managed-project-routes.js";
|
|
2
4
|
import { existsSync, statSync } from "node:fs";
|
|
3
5
|
import { resolve } from "node:path";
|
|
4
6
|
import fastify from "fastify";
|
|
7
|
+
import { createIdempotencyKey } from "@tutti/shared/ids";
|
|
5
8
|
import { resolveTuttiHome } from "../../providers/openai/index.js";
|
|
6
9
|
import { readCliVersion } from "../cli/version.js";
|
|
7
10
|
import { registerHostWebStaticRoutes, resolvePackagedWebStaticRoot } from "../http/static-web.js";
|
|
@@ -66,7 +69,11 @@ function requireLoopbackHost(request) {
|
|
|
66
69
|
}
|
|
67
70
|
}
|
|
68
71
|
export function createLocalConsoleServer(options) {
|
|
69
|
-
const app = fastify({
|
|
72
|
+
const app = fastify({
|
|
73
|
+
logger: false,
|
|
74
|
+
bodyLimit: 64 * 1024,
|
|
75
|
+
genReqId: () => createIdempotencyKey(),
|
|
76
|
+
});
|
|
70
77
|
const tuttiHome = options.tuttiHome ?? resolveTuttiHome(options.env?.TUTTI_HOME, options.cwd);
|
|
71
78
|
const sessions = new LocalConsoleSessionRegistry({
|
|
72
79
|
...(options.now === undefined ? {} : { now: options.now }),
|
|
@@ -119,6 +126,7 @@ export function createLocalConsoleServer(options) {
|
|
|
119
126
|
}
|
|
120
127
|
});
|
|
121
128
|
app.addHook("onRequest", (request, reply, done) => {
|
|
129
|
+
reply.header("x-tutti-request-id", request.id);
|
|
122
130
|
try {
|
|
123
131
|
requireLoopbackHost(request);
|
|
124
132
|
void reply
|
|
@@ -135,7 +143,7 @@ export function createLocalConsoleServer(options) {
|
|
|
135
143
|
done(error);
|
|
136
144
|
}
|
|
137
145
|
});
|
|
138
|
-
app.setErrorHandler((error,
|
|
146
|
+
app.setErrorHandler((error, request, reply) => {
|
|
139
147
|
const normalized = error instanceof LocalConsoleHttpError ||
|
|
140
148
|
error instanceof LocalConsoleProjectError ||
|
|
141
149
|
error instanceof LocalConsoleOperationConflictError
|
|
@@ -148,6 +156,7 @@ export function createLocalConsoleServer(options) {
|
|
|
148
156
|
code: normalized.code,
|
|
149
157
|
message: normalized.message,
|
|
150
158
|
retryable: normalized.statusCode >= 500,
|
|
159
|
+
request_id: request.id,
|
|
151
160
|
},
|
|
152
161
|
});
|
|
153
162
|
});
|
|
@@ -173,6 +182,17 @@ export function createLocalConsoleServer(options) {
|
|
|
173
182
|
options.onActivity?.();
|
|
174
183
|
return session;
|
|
175
184
|
}
|
|
185
|
+
registerLocalConsoleManagedProjectRoutes(app, {
|
|
186
|
+
tuttiHome,
|
|
187
|
+
requireSession,
|
|
188
|
+
service: new LocalConsoleManagedProjectService({
|
|
189
|
+
tuttiHome,
|
|
190
|
+
operations,
|
|
191
|
+
projects,
|
|
192
|
+
...(options.env === undefined ? {} : { serviceEnvironment: options.env }),
|
|
193
|
+
...(options.now === undefined ? {} : { now: options.now }),
|
|
194
|
+
}),
|
|
195
|
+
});
|
|
176
196
|
app.get(`${LOCAL_CONSOLE_API_BASE}/health`, () => ({
|
|
177
197
|
status: "ready",
|
|
178
198
|
protocol_version: 2,
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
import type { OpenAiCredentialLease, OpenAiExecutionConfig } from "../../providers/openai/execution-access.js";
|
|
2
|
+
import type { NativeManagedRuntime } from "./runtime.js";
|
|
3
|
+
import type { ModelSessionOwner } from "./runtime-records.js";
|
|
4
|
+
export type ManagedExecutionRuntime = Pick<NativeManagedRuntime, "selection" | "run">;
|
|
5
|
+
export declare function bindManagedExecution(runtime: ManagedExecutionRuntime, owner: ModelSessionOwner): Promise<{
|
|
6
|
+
config: OpenAiExecutionConfig;
|
|
7
|
+
credentialLease: OpenAiCredentialLease;
|
|
8
|
+
}>;
|
|
9
|
+
//# sourceMappingURL=execution-binding.d.ts.map
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
import { ManagedLocalStateError } from "./private-store.js";
|
|
2
|
+
export async function bindManagedExecution(runtime, owner) {
|
|
3
|
+
// This happens before provider factories open any durable Thread. Retaining
|
|
4
|
+
// this selection through retries forbids silent route/model replacement.
|
|
5
|
+
const selected = await runtime.selection(owner);
|
|
6
|
+
const config = {
|
|
7
|
+
default_model: selected.provider.model,
|
|
8
|
+
api_base_url: selected.provider.base_url,
|
|
9
|
+
};
|
|
10
|
+
return {
|
|
11
|
+
config,
|
|
12
|
+
credentialLease: (durationMs, invoke) => runtime.run(owner, Math.ceil(durationMs / 1000), async (access) => {
|
|
13
|
+
if (access.session_ref !== selected.session_ref ||
|
|
14
|
+
access.route_ref !== selected.route_ref ||
|
|
15
|
+
access.provider.model !== selected.provider.model ||
|
|
16
|
+
access.provider.base_url !== selected.provider.base_url)
|
|
17
|
+
throw new ManagedLocalStateError("managed_route_changed");
|
|
18
|
+
return invoke(access.provider.api_key);
|
|
19
|
+
}),
|
|
20
|
+
};
|
|
21
|
+
}
|
|
22
|
+
//# sourceMappingURL=execution-binding.js.map
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import type { ProjectId } from "@tutti/shared/ids";
|
|
2
|
+
export declare function requireStoppedManagedTarget(tuttiHome: string, projectId: ProjectId): void;
|
|
3
|
+
export declare function readExistingManagedTarget(options: {
|
|
4
|
+
tuttiHome: string;
|
|
5
|
+
relayOrigin: string;
|
|
6
|
+
projectId: ProjectId;
|
|
7
|
+
workspaceRoot: string;
|
|
8
|
+
surface: "standalone" | "feishu";
|
|
9
|
+
requireStopped?: boolean;
|
|
10
|
+
}): {
|
|
11
|
+
relayProjectRef: `relay_project_${string}`;
|
|
12
|
+
workspacePath: string;
|
|
13
|
+
hostSecret: string;
|
|
14
|
+
};
|
|
15
|
+
//# sourceMappingURL=existing-project.d.ts.map
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
import { realpathSync } from "node:fs";
|
|
2
|
+
import { readHostRegistrationSecret, readMachineProjectBinding, readMachineRuntimeEndpoint, } from "../cli/machine-local.js";
|
|
3
|
+
import { readProjectIdentity } from "../cli/project-identity.js";
|
|
4
|
+
import { ManagedLocalStateError } from "./private-store.js";
|
|
5
|
+
import { managedRelayOrigin } from "./state.js";
|
|
6
|
+
// A provider cutover must never mutate the authority used by a live Host. A
|
|
7
|
+
// process we cannot prove stopped is rejected; network health failure is not proof.
|
|
8
|
+
export function requireStoppedManagedTarget(tuttiHome, projectId) {
|
|
9
|
+
const endpoint = readMachineRuntimeEndpoint(tuttiHome, projectId);
|
|
10
|
+
if (endpoint === null)
|
|
11
|
+
return;
|
|
12
|
+
try {
|
|
13
|
+
process.kill(endpoint.pid, 0);
|
|
14
|
+
}
|
|
15
|
+
catch (error) {
|
|
16
|
+
if (error.code === "ESRCH")
|
|
17
|
+
return;
|
|
18
|
+
}
|
|
19
|
+
throw new ManagedLocalStateError("managed_project_must_be_stopped");
|
|
20
|
+
}
|
|
21
|
+
export function readExistingManagedTarget(options) {
|
|
22
|
+
if (options.requireStopped !== false)
|
|
23
|
+
requireStoppedManagedTarget(options.tuttiHome, options.projectId);
|
|
24
|
+
const binding = readMachineProjectBinding(options.tuttiHome, options.projectId);
|
|
25
|
+
const identity = readProjectIdentity(options.workspaceRoot);
|
|
26
|
+
if (binding === null ||
|
|
27
|
+
binding.relay_project_ref === undefined ||
|
|
28
|
+
binding.relay_url === undefined ||
|
|
29
|
+
managedRelayOrigin(binding.relay_url) !== options.relayOrigin ||
|
|
30
|
+
realpathSync(binding.workspace_root) !== realpathSync(options.workspaceRoot) ||
|
|
31
|
+
binding.surface_profile.kind !== options.surface ||
|
|
32
|
+
identity.kind !== "present" ||
|
|
33
|
+
identity.project_id !== options.projectId)
|
|
34
|
+
throw new ManagedLocalStateError("managed_existing_project_mismatch");
|
|
35
|
+
return {
|
|
36
|
+
relayProjectRef: binding.relay_project_ref,
|
|
37
|
+
workspacePath: binding.workspace_root,
|
|
38
|
+
hostSecret: readHostRegistrationSecret(options.tuttiHome, options.projectId).secret,
|
|
39
|
+
};
|
|
40
|
+
}
|
|
41
|
+
//# sourceMappingURL=existing-project.js.map
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
// The only compatibility boundary for 0.1.120 private records. This is not a
|
|
2
|
+
// network parser: callers still validate the complete current strict schema.
|
|
3
|
+
export function upgradeTieredPrivateRecord(value, withAuthority = false) {
|
|
4
|
+
if (!value || typeof value !== "object" || !("version" in value) || value.version !== 1)
|
|
5
|
+
return value;
|
|
6
|
+
if (!("tier" in value) ||
|
|
7
|
+
typeof value.tier !== "string" ||
|
|
8
|
+
!["light", "high", "max"].includes(value.tier))
|
|
9
|
+
return value;
|
|
10
|
+
const { tier, ...fields } = value;
|
|
11
|
+
if (withAuthority && "authority" in fields) {
|
|
12
|
+
const authority = fields.authority;
|
|
13
|
+
if (!authority ||
|
|
14
|
+
typeof authority !== "object" ||
|
|
15
|
+
!("tier" in authority) ||
|
|
16
|
+
authority.tier !== tier)
|
|
17
|
+
return value;
|
|
18
|
+
const projection = { ...authority };
|
|
19
|
+
delete projection.tier;
|
|
20
|
+
return { ...fields, version: 2, authority: projection };
|
|
21
|
+
}
|
|
22
|
+
return { ...fields, version: 2 };
|
|
23
|
+
}
|
|
24
|
+
//# sourceMappingURL=legacy-records.js.map
|
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
import { ManagedLocalStateError } from "./private-store.js";
|
|
2
|
+
export declare class ManagedControlError extends ManagedLocalStateError {
|
|
3
|
+
readonly status?: number | undefined;
|
|
4
|
+
readonly requestId?: string | undefined;
|
|
5
|
+
constructor(code: string, status?: number | undefined, requestId?: string | undefined);
|
|
6
|
+
}
|
|
7
|
+
export declare class NativeModelControlClient {
|
|
8
|
+
private readonly options;
|
|
9
|
+
constructor(options: {
|
|
10
|
+
authorize: () => Promise<{
|
|
11
|
+
authorization: string;
|
|
12
|
+
control_origin: string;
|
|
13
|
+
}>;
|
|
14
|
+
fetchImpl?: typeof fetch;
|
|
15
|
+
});
|
|
16
|
+
private request;
|
|
17
|
+
pin(sessionRef: string, routeRef?: string): Promise<{
|
|
18
|
+
disposition: "created" | "existing";
|
|
19
|
+
session_ref: string;
|
|
20
|
+
provider: {
|
|
21
|
+
model: string;
|
|
22
|
+
base_url: string;
|
|
23
|
+
};
|
|
24
|
+
route_ref: string;
|
|
25
|
+
}>;
|
|
26
|
+
issue(sessionRef: string, requestRef: string): Promise<{
|
|
27
|
+
disposition: "issued";
|
|
28
|
+
provider: {
|
|
29
|
+
api_key: string;
|
|
30
|
+
model: string;
|
|
31
|
+
base_url: string;
|
|
32
|
+
};
|
|
33
|
+
route_ref: string;
|
|
34
|
+
credential: {
|
|
35
|
+
state: "active" | "revoked" | "expired" | "creating" | "revoking";
|
|
36
|
+
session_ref: string;
|
|
37
|
+
expires_at: number;
|
|
38
|
+
credential_ref: string;
|
|
39
|
+
device_ref: string;
|
|
40
|
+
quota_cap: number;
|
|
41
|
+
};
|
|
42
|
+
} | {
|
|
43
|
+
disposition: "already_issued";
|
|
44
|
+
credential: {
|
|
45
|
+
state: "active" | "revoked" | "expired" | "creating" | "revoking";
|
|
46
|
+
session_ref: string;
|
|
47
|
+
expires_at: number;
|
|
48
|
+
credential_ref: string;
|
|
49
|
+
device_ref: string;
|
|
50
|
+
quota_cap: number;
|
|
51
|
+
};
|
|
52
|
+
}>;
|
|
53
|
+
revoke(credentialRef: string): Promise<{
|
|
54
|
+
disposition: "revoked";
|
|
55
|
+
credential: {
|
|
56
|
+
state: "active" | "revoked" | "expired" | "creating" | "revoking";
|
|
57
|
+
session_ref: string;
|
|
58
|
+
expires_at: number;
|
|
59
|
+
credential_ref: string;
|
|
60
|
+
device_ref: string;
|
|
61
|
+
quota_cap: number;
|
|
62
|
+
};
|
|
63
|
+
}>;
|
|
64
|
+
}
|
|
65
|
+
//# sourceMappingURL=model-control-client.d.ts.map
|
|
@@ -0,0 +1,112 @@
|
|
|
1
|
+
import { Value } from "@sinclair/typebox/value";
|
|
2
|
+
import { ManagedSessionResultSchema, ManagedCredentialIssueResultSchema, ManagedCredentialRevokeResultSchema, ManagedRuntimeCredentialRefSchema, } from "@tutti/shared/schemas/internal";
|
|
3
|
+
import { managedRelayOrigin } from "./state.js";
|
|
4
|
+
import { ManagedLocalStateError } from "./private-store.js";
|
|
5
|
+
export class ManagedControlError extends ManagedLocalStateError {
|
|
6
|
+
status;
|
|
7
|
+
requestId;
|
|
8
|
+
constructor(code, status, requestId) {
|
|
9
|
+
super(code);
|
|
10
|
+
this.status = status;
|
|
11
|
+
this.requestId = requestId;
|
|
12
|
+
}
|
|
13
|
+
}
|
|
14
|
+
const reasons = new Set([
|
|
15
|
+
"authority_invalid",
|
|
16
|
+
"host_delegation_revoked",
|
|
17
|
+
"delegated_installation_fixed",
|
|
18
|
+
"session_not_found",
|
|
19
|
+
"default_route_unavailable",
|
|
20
|
+
"pinned_route_unavailable",
|
|
21
|
+
"gateway_route_drift",
|
|
22
|
+
"credential_request_mismatch",
|
|
23
|
+
"credential_creation_unknown",
|
|
24
|
+
"credential_policy_mismatch",
|
|
25
|
+
"device_credential_exists",
|
|
26
|
+
"credential_limit",
|
|
27
|
+
"credential_revocation_unknown",
|
|
28
|
+
"gateway_transport_unknown",
|
|
29
|
+
"gateway_rate_limited",
|
|
30
|
+
"gateway_rejected",
|
|
31
|
+
"gateway_response_invalid",
|
|
32
|
+
"control_queue_full",
|
|
33
|
+
]);
|
|
34
|
+
export class NativeModelControlClient {
|
|
35
|
+
options;
|
|
36
|
+
constructor(options) {
|
|
37
|
+
this.options = options;
|
|
38
|
+
}
|
|
39
|
+
async request(path, body, schema) {
|
|
40
|
+
const authority = await this.options.authorize();
|
|
41
|
+
try {
|
|
42
|
+
const origin = managedRelayOrigin(authority.control_origin);
|
|
43
|
+
const response = await (this.options.fetchImpl ?? fetch)(new URL(path, origin), {
|
|
44
|
+
method: "POST",
|
|
45
|
+
redirect: "error",
|
|
46
|
+
signal: AbortSignal.timeout(55_000),
|
|
47
|
+
headers: {
|
|
48
|
+
"content-type": "application/json",
|
|
49
|
+
authorization: `Bearer ${authority.authorization}`,
|
|
50
|
+
},
|
|
51
|
+
body: JSON.stringify(body),
|
|
52
|
+
});
|
|
53
|
+
const header = response.headers.get("x-tutti-request-id");
|
|
54
|
+
const requestId = header && /^[A-Za-z0-9_-]{1,100}$/.test(header) ? header : undefined;
|
|
55
|
+
const reader = response.body?.getReader();
|
|
56
|
+
if (!reader)
|
|
57
|
+
throw new ManagedControlError("managed_control_invalid_response", response.status, requestId);
|
|
58
|
+
const chunks = [];
|
|
59
|
+
let size = 0;
|
|
60
|
+
try {
|
|
61
|
+
for (;;) {
|
|
62
|
+
const item = await reader.read();
|
|
63
|
+
if (item.done)
|
|
64
|
+
break;
|
|
65
|
+
if (!(item.value instanceof Uint8Array))
|
|
66
|
+
throw new ManagedControlError("managed_control_invalid_response", response.status, requestId);
|
|
67
|
+
size += item.value.byteLength;
|
|
68
|
+
if (size > 16 * 1024)
|
|
69
|
+
throw new ManagedControlError("managed_control_invalid_response", response.status, requestId);
|
|
70
|
+
chunks.push(item.value);
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
finally {
|
|
74
|
+
await reader.cancel().catch(() => undefined);
|
|
75
|
+
reader.releaseLock();
|
|
76
|
+
}
|
|
77
|
+
let value;
|
|
78
|
+
try {
|
|
79
|
+
value = JSON.parse(Buffer.concat(chunks).toString("utf8"));
|
|
80
|
+
}
|
|
81
|
+
catch {
|
|
82
|
+
throw new ManagedControlError("managed_control_invalid_response", response.status, requestId);
|
|
83
|
+
}
|
|
84
|
+
if (!response.ok) {
|
|
85
|
+
const reason = value?.error?.code;
|
|
86
|
+
throw new ManagedControlError(typeof reason === "string" && reasons.has(reason)
|
|
87
|
+
? reason
|
|
88
|
+
: "managed_control_unavailable", response.status, requestId);
|
|
89
|
+
}
|
|
90
|
+
if (!Value.Check(schema, value))
|
|
91
|
+
throw new ManagedControlError("managed_control_invalid_response", response.status, requestId);
|
|
92
|
+
return value;
|
|
93
|
+
}
|
|
94
|
+
catch (error) {
|
|
95
|
+
if (error instanceof ManagedLocalStateError)
|
|
96
|
+
throw error;
|
|
97
|
+
throw new ManagedControlError("managed_control_unavailable");
|
|
98
|
+
}
|
|
99
|
+
}
|
|
100
|
+
pin(sessionRef, routeRef) {
|
|
101
|
+
return this.request("/v2/sessions", { session_ref: sessionRef, ...(routeRef === undefined ? {} : { route_ref: routeRef }) }, ManagedSessionResultSchema);
|
|
102
|
+
}
|
|
103
|
+
issue(sessionRef, requestRef) {
|
|
104
|
+
return this.request("/v2/credentials", { session_ref: sessionRef, request_ref: requestRef }, ManagedCredentialIssueResultSchema);
|
|
105
|
+
}
|
|
106
|
+
revoke(credentialRef) {
|
|
107
|
+
if (!Value.Check(ManagedRuntimeCredentialRefSchema, credentialRef))
|
|
108
|
+
throw new ManagedControlError("managed_control_invalid_request");
|
|
109
|
+
return this.request(`/v2/credentials/${credentialRef}/revoke`, {}, ManagedCredentialRevokeResultSchema);
|
|
110
|
+
}
|
|
111
|
+
}
|
|
112
|
+
//# sourceMappingURL=model-control-client.js.map
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
export declare class ManagedLocalStateError extends Error {
|
|
2
|
+
readonly code: string;
|
|
3
|
+
constructor(code: string);
|
|
4
|
+
}
|
|
5
|
+
export declare function isWithin(parent: string, target: string): boolean;
|
|
6
|
+
export declare function ensureManagedPrivateRoot(tuttiHome: string, originHash: string): string;
|
|
7
|
+
export declare class ManagedPrivateStore {
|
|
8
|
+
readonly root: string;
|
|
9
|
+
private readonly identity;
|
|
10
|
+
constructor(root: string);
|
|
11
|
+
private checkRoot;
|
|
12
|
+
private path;
|
|
13
|
+
read(name: string): unknown;
|
|
14
|
+
write(name: string, value: unknown, exclusive?: boolean): boolean;
|
|
15
|
+
transaction<T>(run: () => T): T;
|
|
16
|
+
acquireExecutionLease(name: string): () => void;
|
|
17
|
+
private syncDirectory;
|
|
18
|
+
}
|
|
19
|
+
//# sourceMappingURL=private-store.d.ts.map
|