@xfey/tutti 0.1.11 → 0.1.13
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/control-plane/context-sync.js +1 -0
- package/dist/control-plane/event-publishers.js +21 -12
- package/dist/control-plane/execution-status.js +4 -0
- package/dist/control-plane/project-context-bootstrap.d.ts +2 -0
- package/dist/control-plane/project-context-bootstrap.js +1 -0
- package/dist/control-plane/reference-summary-refresh.js +4 -0
- package/dist/control-plane/run-start.js +1 -0
- package/dist/control-plane/task-compile-continuation.js +1 -0
- package/dist/control-plane/task-compile-start.d.ts +2 -1
- package/dist/control-plane/task-compile-start.js +5 -0
- package/dist/control-plane/types.d.ts +2 -1
- package/dist/control-plane/workflows/openai.js +19 -0
- package/dist/control-plane/workflows/types.d.ts +6 -1
- package/dist/procedure-engine/index.d.ts +10 -1
- package/dist/procedure-engine/index.js +34 -4
- package/dist/providers/openai/app-server/json-rpc.d.ts +6 -1
- package/dist/providers/openai/app-server/json-rpc.js +15 -1
- package/dist/providers/openai/app-server/read-only-procedure.d.ts +2 -0
- package/dist/providers/openai/app-server/read-only-procedure.js +13 -1
- package/dist/providers/openai/app-server/runtime-telemetry.d.ts +18 -0
- package/dist/providers/openai/app-server/runtime-telemetry.js +130 -0
- package/dist/providers/openai/app-server/workspace-write-run.d.ts +2 -0
- package/dist/providers/openai/app-server/workspace-write-run.js +13 -1
- package/dist/run-engine/index.d.ts +2 -1
- package/dist/run-engine/index.js +14 -1
- package/dist/run-pipeline/task-run-invocation.js +9 -0
- package/dist/server-shell/cli/args.d.ts +9 -9
- package/dist/server-shell/cli/args.js +31 -31
- package/dist/server-shell/cli/cli.js +24 -24
- package/dist/server-shell/cli/errors.d.ts +1 -1
- package/dist/server-shell/cli/host-lifecycle.js +7 -0
- package/dist/server-shell/cli/launch-command.d.ts +2 -2
- package/dist/server-shell/cli/launch-command.js +24 -3
- package/dist/server-shell/cli/managed-host.d.ts +2 -0
- package/dist/server-shell/cli/managed-host.js +6 -0
- package/dist/server-shell/cli/project-resolver.d.ts +6 -0
- package/dist/server-shell/cli/project-resolver.js +125 -3
- package/dist/server-shell/cli/runtime-commands.d.ts +5 -5
- package/dist/server-shell/cli/runtime-commands.js +50 -19
- package/dist/server-shell/cli/terminal-qr.d.ts +5 -0
- package/dist/server-shell/cli/terminal-qr.js +17 -0
- package/dist/server-shell/http/routes/project-api/openapi-execution-routes.d.ts +14 -0
- package/dist/server-shell/http/routes/project-api/openapi-workspace-routes.d.ts +14 -0
- package/dist/server-shell/http/routes/project-api/openapi.d.ts +28 -0
- package/dist/server-shell/http/routes/project-api/schemas.d.ts +14 -0
- package/node_modules/@tutti/shared/dist/schemas/api/runtime-events.d.ts +50 -0
- package/node_modules/@tutti/shared/dist/schemas/api/runtime-events.js +25 -0
- package/node_modules/@tutti/shared/dist/schemas/api/types.d.ts +4 -1
- package/package.json +2 -1
- package/web/assets/index-CDUT1Etl.js +29 -0
- package/web/assets/{index-D8xJY8Gr.css → index-DIQpGeKY.css} +1 -1
- package/web/index.html +2 -2
- package/web/assets/index-BMMQgQjz.js +0 -29
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import type { ActivityRef, TaskId } from "@tutti/shared/ids";
|
|
2
2
|
import type { CodexAppServerApprovalPolicy } from "../codex-app-server.js";
|
|
3
3
|
import { type CodexAppServerJsonRpcError, type CodexAppServerJsonRpcServerRequest } from "./json-rpc.js";
|
|
4
|
+
import { type CodexAppServerRuntimeTelemetryOptions } from "./runtime-telemetry.js";
|
|
4
5
|
import { type CodexAppServerAgentContextRuntime, type CodexAppServerSkillSelectionName } from "./skills.js";
|
|
5
6
|
import { type OpenAiTokenUsage } from "../token-usage.js";
|
|
6
7
|
export type CodexAppServerApprovalsReviewer = "user" | "auto_review" | "guardian_subagent";
|
|
@@ -53,6 +54,7 @@ export type CodexAppServerWorkspaceWriteRunInput<TOutput> = {
|
|
|
53
54
|
requestTimeoutMs?: number;
|
|
54
55
|
turnTimeoutMs?: number;
|
|
55
56
|
baseEnv?: NodeJS.ProcessEnv;
|
|
57
|
+
runtimeTelemetry?: CodexAppServerRuntimeTelemetryOptions;
|
|
56
58
|
};
|
|
57
59
|
export type CodexAppServerWorkspaceWriteRunResult<TOutput> = {
|
|
58
60
|
output: TOutput;
|
|
@@ -5,6 +5,7 @@ import { join } from "node:path";
|
|
|
5
5
|
import { buildCodexAppServerProcessPlan } from "../codex-app-server.js";
|
|
6
6
|
import { DEFAULT_OPENAI_MODEL } from "../model-config.js";
|
|
7
7
|
import { CodexAppServerJsonRpcClient, CodexAppServerProtocolError, } from "./json-rpc.js";
|
|
8
|
+
import { createCodexAppServerRuntimeObserver, } from "./runtime-telemetry.js";
|
|
8
9
|
import { createWorkspaceWriteSandboxPolicy } from "./sandbox-policy.js";
|
|
9
10
|
import { buildCodexAppServerProviderConfigOverrides, resolveCodexAppServerModelProvider, } from "./provider-config.js";
|
|
10
11
|
import { buildCodexAppServerTurnInput, resolveCodexAppServerContextRuntimeHint, resolveCodexAppServerSkillsListWarmup, resolveCodexAppServerSkillInputs, CodexAppServerSkillSelectionError, } from "./skills.js";
|
|
@@ -201,7 +202,17 @@ export async function runCodexAppServerWorkspaceWriteRun(options) {
|
|
|
201
202
|
env: createCodexSpawnEnv(codexHome, options.baseEnv ?? process.env),
|
|
202
203
|
stdio: "pipe",
|
|
203
204
|
});
|
|
204
|
-
const
|
|
205
|
+
const runtimeObserver = options.runtimeTelemetry === undefined
|
|
206
|
+
? undefined
|
|
207
|
+
: createCodexAppServerRuntimeObserver(options.runtimeTelemetry);
|
|
208
|
+
const client = new CodexAppServerJsonRpcClient(child, {
|
|
209
|
+
...(runtimeObserver === undefined
|
|
210
|
+
? {}
|
|
211
|
+
: {
|
|
212
|
+
onNotification: runtimeObserver.notification,
|
|
213
|
+
onServerRequest: runtimeObserver.serverRequest,
|
|
214
|
+
}),
|
|
215
|
+
});
|
|
205
216
|
const sensitiveValues = [
|
|
206
217
|
options.apiKey,
|
|
207
218
|
tempRoot,
|
|
@@ -370,6 +381,7 @@ export async function runCodexAppServerWorkspaceWriteRun(options) {
|
|
|
370
381
|
throw new CodexAppServerWorkspaceWriteRunError("app_server_protocol_error", redactWithKnownValues(message, sensitiveValues));
|
|
371
382
|
}
|
|
372
383
|
finally {
|
|
384
|
+
runtimeObserver?.flush();
|
|
373
385
|
client.dispose();
|
|
374
386
|
rmSync(tempRoot, { recursive: true, force: true });
|
|
375
387
|
}
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { type ActivityRef, type TaskId } from "@tutti/shared/ids";
|
|
2
2
|
import type { RunLineage } from "@tutti/shared/domain";
|
|
3
|
-
import type { ActivityProjection, ExecutionStatusProjection, TaskProjection } from "@tutti/shared/schemas/api";
|
|
3
|
+
import type { ActivityProjection, ExecutionRuntimeSnapshot, ExecutionStatusProjection, TaskProjection } from "@tutti/shared/schemas/api";
|
|
4
4
|
export type RunTerminalConclusion = {
|
|
5
5
|
kind: "completed";
|
|
6
6
|
summary: string;
|
|
@@ -20,6 +20,7 @@ export type RunExecutionContext = {
|
|
|
20
20
|
export type RunProgressUpdate = {
|
|
21
21
|
summary: string;
|
|
22
22
|
phase?: string;
|
|
23
|
+
runtime?: ExecutionRuntimeSnapshot;
|
|
23
24
|
};
|
|
24
25
|
export type StartRunOptions = {
|
|
25
26
|
activity_ref?: ActivityRef;
|
package/dist/run-engine/index.js
CHANGED
|
@@ -57,7 +57,7 @@ export class RunEngine {
|
|
|
57
57
|
}
|
|
58
58
|
getExecutionStatus() {
|
|
59
59
|
if (this.active !== null) {
|
|
60
|
-
|
|
60
|
+
const status = {
|
|
61
61
|
status: "running",
|
|
62
62
|
summary: this.active.activity.summary,
|
|
63
63
|
active_task: this.active.task,
|
|
@@ -70,6 +70,10 @@ export class RunEngine {
|
|
|
70
70
|
},
|
|
71
71
|
updated_at: this.now().toISOString(),
|
|
72
72
|
};
|
|
73
|
+
if (this.active.runtime !== undefined) {
|
|
74
|
+
status.runtime = this.active.runtime;
|
|
75
|
+
}
|
|
76
|
+
return status;
|
|
73
77
|
}
|
|
74
78
|
const idle = {
|
|
75
79
|
status: "idle",
|
|
@@ -106,6 +110,15 @@ export class RunEngine {
|
|
|
106
110
|
return;
|
|
107
111
|
}
|
|
108
112
|
active.activity.summary = progress.summary;
|
|
113
|
+
if (progress.runtime !== undefined) {
|
|
114
|
+
active.runtime = progress.runtime;
|
|
115
|
+
}
|
|
116
|
+
else if (progress.phase !== undefined && active.runtime !== undefined) {
|
|
117
|
+
active.runtime = {
|
|
118
|
+
...active.runtime,
|
|
119
|
+
stage_id: progress.phase,
|
|
120
|
+
};
|
|
121
|
+
}
|
|
109
122
|
onTransition?.(this.state("progress", progress));
|
|
110
123
|
}
|
|
111
124
|
finishActive(activityRef, conclusion, onTransition) {
|
|
@@ -109,6 +109,15 @@ export async function runWorkspaceWriteTask(options, input, handle) {
|
|
|
109
109
|
: { requestTimeoutMs: options.requestTimeoutMs }),
|
|
110
110
|
...(options.turnTimeoutMs === undefined ? {} : { turnTimeoutMs: options.turnTimeoutMs }),
|
|
111
111
|
...(options.baseEnv === undefined ? {} : { baseEnv: options.baseEnv }),
|
|
112
|
+
runtimeTelemetry: {
|
|
113
|
+
activityRef: input.activity_ref,
|
|
114
|
+
stageId: "provider_task_run",
|
|
115
|
+
onSnapshot: (snapshot) => input.on_stage?.({
|
|
116
|
+
stage: "provider_task_run",
|
|
117
|
+
summary: "running Codex task implementation.",
|
|
118
|
+
runtime: snapshot,
|
|
119
|
+
}),
|
|
120
|
+
},
|
|
112
121
|
});
|
|
113
122
|
}
|
|
114
123
|
finally {
|
|
@@ -5,7 +5,7 @@ export type CliCommand = {
|
|
|
5
5
|
kind: "version";
|
|
6
6
|
} | {
|
|
7
7
|
kind: "launch";
|
|
8
|
-
|
|
8
|
+
target?: string;
|
|
9
9
|
yes: boolean;
|
|
10
10
|
foreground: boolean;
|
|
11
11
|
} | {
|
|
@@ -17,29 +17,29 @@ export type CliCommand = {
|
|
|
17
17
|
manage: boolean;
|
|
18
18
|
} | {
|
|
19
19
|
kind: "stop";
|
|
20
|
-
|
|
20
|
+
target?: string;
|
|
21
21
|
} | {
|
|
22
22
|
kind: "invite";
|
|
23
|
-
|
|
23
|
+
target?: string;
|
|
24
24
|
} | {
|
|
25
25
|
kind: "logs";
|
|
26
|
-
|
|
26
|
+
target?: string;
|
|
27
27
|
tail: number;
|
|
28
28
|
} | {
|
|
29
29
|
kind: "provider-setup";
|
|
30
|
-
|
|
30
|
+
target?: string;
|
|
31
31
|
} | {
|
|
32
32
|
kind: "provider-status";
|
|
33
|
-
|
|
33
|
+
target?: string;
|
|
34
34
|
} | {
|
|
35
35
|
kind: "doctor";
|
|
36
|
-
|
|
36
|
+
target?: string;
|
|
37
37
|
};
|
|
38
38
|
export type LaunchCliArgs = {
|
|
39
39
|
command: "launch";
|
|
40
|
-
|
|
40
|
+
target?: string;
|
|
41
41
|
yes: boolean;
|
|
42
42
|
};
|
|
43
|
-
export declare function parseLaunchCliArgs(argv: readonly string[]
|
|
43
|
+
export declare function parseLaunchCliArgs(argv: readonly string[]): LaunchCliArgs;
|
|
44
44
|
export declare function parseCliArgs(argv: readonly string[], cwd?: string): CliCommand;
|
|
45
45
|
//# sourceMappingURL=args.d.ts.map
|
|
@@ -6,13 +6,13 @@ function isHelpArg(arg) {
|
|
|
6
6
|
function hasHelpArg(args) {
|
|
7
7
|
return args.some((arg) => isHelpArg(arg));
|
|
8
8
|
}
|
|
9
|
-
export function parseLaunchCliArgs(argv
|
|
9
|
+
export function parseLaunchCliArgs(argv) {
|
|
10
10
|
const args = [...argv];
|
|
11
11
|
const command = args.shift();
|
|
12
12
|
if (command !== "launch") {
|
|
13
|
-
throw new LaunchError("git_bootstrap_failed", "Expected command: launch", "Run `tutti launch [
|
|
13
|
+
throw new LaunchError("git_bootstrap_failed", "Expected command: launch", "Run `tutti launch [target] [--yes]`.");
|
|
14
14
|
}
|
|
15
|
-
let
|
|
15
|
+
let target;
|
|
16
16
|
let yes = false;
|
|
17
17
|
for (const arg of args) {
|
|
18
18
|
if (arg === "--yes") {
|
|
@@ -22,29 +22,29 @@ export function parseLaunchCliArgs(argv, cwd = process.cwd()) {
|
|
|
22
22
|
if (arg.startsWith("--")) {
|
|
23
23
|
throw new LaunchError("git_bootstrap_failed", `Unsupported option: ${arg}`, "Use only `--yes` with `tutti launch` in the MVP command surface.");
|
|
24
24
|
}
|
|
25
|
-
if (
|
|
26
|
-
throw new LaunchError("git_bootstrap_failed", "Only one
|
|
25
|
+
if (target !== undefined) {
|
|
26
|
+
throw new LaunchError("git_bootstrap_failed", "Only one target argument is supported", "Run `tutti launch [target] [--yes]`.");
|
|
27
27
|
}
|
|
28
|
-
|
|
28
|
+
target = arg;
|
|
29
29
|
}
|
|
30
30
|
return {
|
|
31
31
|
command: "launch",
|
|
32
|
-
|
|
32
|
+
...(target === undefined ? {} : { target }),
|
|
33
33
|
yes,
|
|
34
34
|
};
|
|
35
35
|
}
|
|
36
|
-
function
|
|
37
|
-
let
|
|
36
|
+
function readOptionalTarget(args) {
|
|
37
|
+
let target;
|
|
38
38
|
for (const arg of args) {
|
|
39
39
|
if (arg.startsWith("--")) {
|
|
40
40
|
throw new LaunchError("git_bootstrap_failed", `Unsupported option: ${arg}`, "Run `tutti --help` to see supported commands.");
|
|
41
41
|
}
|
|
42
|
-
if (
|
|
43
|
-
throw new LaunchError("git_bootstrap_failed", "Only one
|
|
42
|
+
if (target !== undefined) {
|
|
43
|
+
throw new LaunchError("git_bootstrap_failed", "Only one target argument is supported", "Run `tutti --help` to see supported commands.");
|
|
44
44
|
}
|
|
45
|
-
|
|
45
|
+
target = arg;
|
|
46
46
|
}
|
|
47
|
-
return
|
|
47
|
+
return target;
|
|
48
48
|
}
|
|
49
49
|
export function parseCliArgs(argv, cwd = process.cwd()) {
|
|
50
50
|
const args = [...argv];
|
|
@@ -59,7 +59,7 @@ export function parseCliArgs(argv, cwd = process.cwd()) {
|
|
|
59
59
|
return { kind: "version" };
|
|
60
60
|
}
|
|
61
61
|
if (command === "launch") {
|
|
62
|
-
let
|
|
62
|
+
let target;
|
|
63
63
|
let yes = false;
|
|
64
64
|
let foreground = false;
|
|
65
65
|
for (const arg of args) {
|
|
@@ -77,12 +77,12 @@ export function parseCliArgs(argv, cwd = process.cwd()) {
|
|
|
77
77
|
if (arg.startsWith("--")) {
|
|
78
78
|
throw new LaunchError("git_bootstrap_failed", `Unsupported option: ${arg}`, "Use `tutti launch --help` to see supported launch options.");
|
|
79
79
|
}
|
|
80
|
-
if (
|
|
81
|
-
throw new LaunchError("git_bootstrap_failed", "Only one
|
|
80
|
+
if (target !== undefined) {
|
|
81
|
+
throw new LaunchError("git_bootstrap_failed", "Only one target argument is supported", "Run `tutti launch [target] [--yes] [--foreground]`.");
|
|
82
82
|
}
|
|
83
|
-
|
|
83
|
+
target = arg;
|
|
84
84
|
}
|
|
85
|
-
return { kind: "launch",
|
|
85
|
+
return { kind: "launch", ...(target === undefined ? {} : { target }), yes, foreground };
|
|
86
86
|
}
|
|
87
87
|
if (command === "internal") {
|
|
88
88
|
const internalCommand = args.shift();
|
|
@@ -131,15 +131,15 @@ export function parseCliArgs(argv, cwd = process.cwd()) {
|
|
|
131
131
|
if (hasHelpArg(args)) {
|
|
132
132
|
return { kind: "help", topic: "stop" };
|
|
133
133
|
}
|
|
134
|
-
const
|
|
135
|
-
return { kind: "stop", ...(
|
|
134
|
+
const target = readOptionalTarget(args);
|
|
135
|
+
return { kind: "stop", ...(target === undefined ? {} : { target }) };
|
|
136
136
|
}
|
|
137
137
|
if (command === "invite") {
|
|
138
138
|
if (hasHelpArg(args)) {
|
|
139
139
|
return { kind: "help", topic: "invite" };
|
|
140
140
|
}
|
|
141
|
-
const
|
|
142
|
-
return { kind: "invite", ...(
|
|
141
|
+
const target = readOptionalTarget(args);
|
|
142
|
+
return { kind: "invite", ...(target === undefined ? {} : { target }) };
|
|
143
143
|
}
|
|
144
144
|
if (command === "logs") {
|
|
145
145
|
if (hasHelpArg(args)) {
|
|
@@ -153,7 +153,7 @@ export function parseCliArgs(argv, cwd = process.cwd()) {
|
|
|
153
153
|
const value = args[index + 1];
|
|
154
154
|
const parsed = Number(value);
|
|
155
155
|
if (!Number.isInteger(parsed) || parsed <= 0) {
|
|
156
|
-
throw new LaunchError("git_bootstrap_failed", "--tail requires a positive integer", "Run `tutti logs [
|
|
156
|
+
throw new LaunchError("git_bootstrap_failed", "--tail requires a positive integer", "Run `tutti logs [target] --tail 120`.");
|
|
157
157
|
}
|
|
158
158
|
tail = parsed;
|
|
159
159
|
index += 1;
|
|
@@ -162,8 +162,8 @@ export function parseCliArgs(argv, cwd = process.cwd()) {
|
|
|
162
162
|
positional.push(arg ?? "");
|
|
163
163
|
}
|
|
164
164
|
}
|
|
165
|
-
const
|
|
166
|
-
return { kind: "logs", tail, ...(
|
|
165
|
+
const target = readOptionalTarget(positional);
|
|
166
|
+
return { kind: "logs", tail, ...(target === undefined ? {} : { target }) };
|
|
167
167
|
}
|
|
168
168
|
if (command === "provider") {
|
|
169
169
|
const providerCommand = args.shift();
|
|
@@ -174,15 +174,15 @@ export function parseCliArgs(argv, cwd = process.cwd()) {
|
|
|
174
174
|
if (hasHelpArg(args)) {
|
|
175
175
|
return { kind: "help", topic: "provider" };
|
|
176
176
|
}
|
|
177
|
-
const
|
|
178
|
-
return { kind: "provider-setup", ...(
|
|
177
|
+
const target = readOptionalTarget(args);
|
|
178
|
+
return { kind: "provider-setup", ...(target === undefined ? {} : { target }) };
|
|
179
179
|
}
|
|
180
180
|
if (providerCommand === "status") {
|
|
181
181
|
if (hasHelpArg(args)) {
|
|
182
182
|
return { kind: "help", topic: "provider" };
|
|
183
183
|
}
|
|
184
|
-
const
|
|
185
|
-
return { kind: "provider-status", ...(
|
|
184
|
+
const target = readOptionalTarget(args);
|
|
185
|
+
return { kind: "provider-status", ...(target === undefined ? {} : { target }) };
|
|
186
186
|
}
|
|
187
187
|
return { kind: "help", topic: "provider" };
|
|
188
188
|
}
|
|
@@ -190,8 +190,8 @@ export function parseCliArgs(argv, cwd = process.cwd()) {
|
|
|
190
190
|
if (hasHelpArg(args)) {
|
|
191
191
|
return { kind: "help", topic: "doctor" };
|
|
192
192
|
}
|
|
193
|
-
const
|
|
194
|
-
return { kind: "doctor", ...(
|
|
193
|
+
const target = readOptionalTarget(args);
|
|
194
|
+
return { kind: "doctor", ...(target === undefined ? {} : { target }) };
|
|
195
195
|
}
|
|
196
196
|
throw new LaunchError("git_bootstrap_failed", `Unsupported command: ${command}`, "Run `tutti --help` to see supported commands.");
|
|
197
197
|
}
|
|
@@ -7,14 +7,14 @@ import { runProviderSetupTui } from "./provider-tui.js";
|
|
|
7
7
|
import { runInviteCommand, runLogsCommand, runProviderStatusCommand, runPsManageCommand, runPsCommand, runStopCommand, } from "./runtime-commands.js";
|
|
8
8
|
import { readCliVersion } from "./version.js";
|
|
9
9
|
const HELP_TEXT = `Usage:
|
|
10
|
-
tutti launch [
|
|
10
|
+
tutti launch [target] [--yes] [--foreground]
|
|
11
11
|
tutti ps [--manage]
|
|
12
|
-
tutti invite [
|
|
13
|
-
tutti stop [
|
|
14
|
-
tutti logs [
|
|
15
|
-
tutti provider setup [
|
|
16
|
-
tutti provider status [
|
|
17
|
-
tutti doctor [
|
|
12
|
+
tutti invite [target]
|
|
13
|
+
tutti stop [target]
|
|
14
|
+
tutti logs [target] [--tail 120]
|
|
15
|
+
tutti provider setup [target]
|
|
16
|
+
tutti provider status [target]
|
|
17
|
+
tutti doctor [target]
|
|
18
18
|
|
|
19
19
|
Options:
|
|
20
20
|
-h, --help Show help.
|
|
@@ -25,7 +25,7 @@ Notes:
|
|
|
25
25
|
provider setup stores credentials in the machine-local Tutti provider store.
|
|
26
26
|
`;
|
|
27
27
|
const LAUNCH_HELP_TEXT = `Usage:
|
|
28
|
-
tutti launch [
|
|
28
|
+
tutti launch [target] [--yes] [--foreground]
|
|
29
29
|
|
|
30
30
|
Starts or reconnects a Tutti project host.
|
|
31
31
|
|
|
@@ -34,8 +34,8 @@ Options:
|
|
|
34
34
|
--foreground Keep the host attached to this terminal for diagnostics.
|
|
35
35
|
`;
|
|
36
36
|
const PROVIDER_HELP_TEXT = `Usage:
|
|
37
|
-
tutti provider setup [
|
|
38
|
-
tutti provider status [
|
|
37
|
+
tutti provider setup [target]
|
|
38
|
+
tutti provider status [target]
|
|
39
39
|
|
|
40
40
|
Commands:
|
|
41
41
|
setup Configure or replace the OpenAI-compatible provider credential.
|
|
@@ -48,22 +48,22 @@ Options:
|
|
|
48
48
|
--manage Open the keyboard project manager.
|
|
49
49
|
`;
|
|
50
50
|
const INVITE_HELP_TEXT = `Usage:
|
|
51
|
-
tutti invite [
|
|
51
|
+
tutti invite [target]
|
|
52
52
|
|
|
53
53
|
Rotates a fresh join link and renders it as a terminal QR code.
|
|
54
54
|
`;
|
|
55
55
|
const STOP_HELP_TEXT = `Usage:
|
|
56
|
-
tutti stop [
|
|
56
|
+
tutti stop [target]
|
|
57
57
|
|
|
58
58
|
Stops the background Tutti host for a project.
|
|
59
59
|
`;
|
|
60
60
|
const LOGS_HELP_TEXT = `Usage:
|
|
61
|
-
tutti logs [
|
|
61
|
+
tutti logs [target] [--tail 120]
|
|
62
62
|
|
|
63
63
|
Shows recent host log lines from the machine-local Tutti log file.
|
|
64
64
|
`;
|
|
65
65
|
const DOCTOR_HELP_TEXT = `Usage:
|
|
66
|
-
tutti doctor [
|
|
66
|
+
tutti doctor [target]
|
|
67
67
|
|
|
68
68
|
Prints local runtime, project, provider, and host diagnostics.
|
|
69
69
|
`;
|
|
@@ -91,9 +91,9 @@ function helpForTopic(topic) {
|
|
|
91
91
|
}
|
|
92
92
|
return HELP_TEXT;
|
|
93
93
|
}
|
|
94
|
-
async function runProviderSetupCommand(
|
|
94
|
+
async function runProviderSetupCommand(target) {
|
|
95
95
|
const project = resolveExistingProjectContext({
|
|
96
|
-
...(
|
|
96
|
+
...(target === undefined ? {} : { target }),
|
|
97
97
|
});
|
|
98
98
|
const result = await runProviderSetupTui({
|
|
99
99
|
tuttiHome: project.tutti_home,
|
|
@@ -108,13 +108,13 @@ async function runProviderSetupCommand(workspacePath) {
|
|
|
108
108
|
].join("\n"));
|
|
109
109
|
process.stdout.write("\n");
|
|
110
110
|
}
|
|
111
|
-
function runDoctorCommand(
|
|
111
|
+
function runDoctorCommand(target) {
|
|
112
112
|
const checks = [];
|
|
113
113
|
checks.push(`Node: ${process.version}`);
|
|
114
114
|
checks.push(`Tutti: ${readCliVersion()}`);
|
|
115
115
|
try {
|
|
116
116
|
const project = resolveExistingProjectContext({
|
|
117
|
-
...(
|
|
117
|
+
...(target === undefined ? {} : { target }),
|
|
118
118
|
});
|
|
119
119
|
checks.push(`Project: ${project.display_name}`);
|
|
120
120
|
checks.push(`Project ID: ${project.project_id}`);
|
|
@@ -156,22 +156,22 @@ try {
|
|
|
156
156
|
}
|
|
157
157
|
break;
|
|
158
158
|
case "stop":
|
|
159
|
-
process.stdout.write(`${await runStopCommand(command.
|
|
159
|
+
process.stdout.write(`${await runStopCommand(command.target)}\n`);
|
|
160
160
|
break;
|
|
161
161
|
case "invite":
|
|
162
|
-
process.stdout.write(`${await runInviteCommand(command.
|
|
162
|
+
process.stdout.write(`${await runInviteCommand(command.target)}\n`);
|
|
163
163
|
break;
|
|
164
164
|
case "logs":
|
|
165
|
-
process.stdout.write(`${runLogsCommand(command.
|
|
165
|
+
process.stdout.write(`${runLogsCommand(command.target, command.tail)}\n`);
|
|
166
166
|
break;
|
|
167
167
|
case "provider-setup":
|
|
168
|
-
await runProviderSetupCommand(command.
|
|
168
|
+
await runProviderSetupCommand(command.target);
|
|
169
169
|
break;
|
|
170
170
|
case "provider-status":
|
|
171
|
-
process.stdout.write(`${runProviderStatusCommand(command.
|
|
171
|
+
process.stdout.write(`${runProviderStatusCommand(command.target)}\n`);
|
|
172
172
|
break;
|
|
173
173
|
case "doctor":
|
|
174
|
-
runDoctorCommand(command.
|
|
174
|
+
runDoctorCommand(command.target);
|
|
175
175
|
break;
|
|
176
176
|
}
|
|
177
177
|
}
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
export type LaunchErrorCode = "unsafe_project_root" | "unsupported_tutti_schema" | "project_identity_conflict" | "git_bootstrap_failed" | "sensitive_file_detected" | "relay_unavailable" | "relay_registration_failed" | "host_not_running" | "provider_configuration_required" | "provider_setup_cancelled" | "provider_validation_failed" | "store_migration_failed" | "existing_server_unhealthy" | "takeover_rejected" | "takeover_failed" | "port_unavailable";
|
|
1
|
+
export type LaunchErrorCode = "unsafe_project_root" | "unsupported_tutti_schema" | "project_identity_conflict" | "git_bootstrap_failed" | "sensitive_file_detected" | "relay_unavailable" | "relay_registration_failed" | "host_not_running" | "project_target_ambiguous" | "project_target_not_found" | "provider_configuration_required" | "provider_setup_cancelled" | "provider_validation_failed" | "store_migration_failed" | "existing_server_unhealthy" | "takeover_rejected" | "takeover_failed" | "port_unavailable";
|
|
2
2
|
export declare class LaunchError extends Error {
|
|
3
3
|
readonly code: LaunchErrorCode;
|
|
4
4
|
readonly next: string;
|
|
@@ -10,6 +10,7 @@ import { HOST_SERVER_VERSION, startForegroundHostServer, } from "./host-server-r
|
|
|
10
10
|
import { deleteMachineRuntimeEndpoint, readHostRegistrationSecret, readMachineProjectBinding, readMachineRuntimeEndpoint, } from "./machine-local.js";
|
|
11
11
|
import { prepareLaunchProject, resolveLaunchLocalContext, } from "./launch.js";
|
|
12
12
|
import { registerHostWithRelay, relayErrorToLaunchError, } from "./relay-registration.js";
|
|
13
|
+
import { formatJoinLinkValidity } from "./terminal-qr.js";
|
|
13
14
|
export { createRuntimeEndpointProbe } from "./host-runtime-endpoint.js";
|
|
14
15
|
const DEFAULT_SHUTDOWN_WAIT_MS = 2_000;
|
|
15
16
|
const OSC8_START = "\u001B]8;;";
|
|
@@ -277,6 +278,12 @@ export function formatLaunchLifecycleResult(result, options = {}) {
|
|
|
277
278
|
}
|
|
278
279
|
if (summary.relay?.join_url !== undefined) {
|
|
279
280
|
fields.push(urlLine("Join URL", summary.relay.join_url, options));
|
|
281
|
+
fields.push(formatJoinLinkValidity({
|
|
282
|
+
...(summary.relay.join_token_expires_at === undefined
|
|
283
|
+
? {}
|
|
284
|
+
: { expiresAt: summary.relay.join_token_expires_at }),
|
|
285
|
+
reusable: summary.relay.join_token_reusable,
|
|
286
|
+
}));
|
|
280
287
|
}
|
|
281
288
|
else if (summary.relay?.join_url_visibility === "not_recoverable") {
|
|
282
289
|
fields.push("Join URL: not recoverable; rotate a fresh invite from the host.");
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import type { LaunchConfirmationRequest } from "./git-bootstrap.js";
|
|
2
2
|
export declare function confirmFromTty(request: LaunchConfirmationRequest): Promise<boolean>;
|
|
3
3
|
export declare function runForegroundLaunchCommand(options: {
|
|
4
|
-
|
|
4
|
+
target?: string;
|
|
5
5
|
yes: boolean;
|
|
6
6
|
}): Promise<void>;
|
|
7
7
|
export declare function runInternalHostRunCommand(options: {
|
|
@@ -9,7 +9,7 @@ export declare function runInternalHostRunCommand(options: {
|
|
|
9
9
|
yes: boolean;
|
|
10
10
|
}): Promise<void>;
|
|
11
11
|
export declare function runBackgroundLaunchCommand(options: {
|
|
12
|
-
|
|
12
|
+
target?: string;
|
|
13
13
|
yes: boolean;
|
|
14
14
|
}): Promise<void>;
|
|
15
15
|
//# sourceMappingURL=launch-command.d.ts.map
|
|
@@ -2,7 +2,8 @@ import { createInterface } from "node:readline/promises";
|
|
|
2
2
|
import { formatLaunchLifecycleResult, startLaunchProject, waitForForegroundHostShutdown } from "./host-lifecycle.js";
|
|
3
3
|
import { prepareLaunchProject } from "./launch.js";
|
|
4
4
|
import { spawnDetachedHost, waitForManagedHostReady } from "./managed-host.js";
|
|
5
|
-
import {
|
|
5
|
+
import { resolveLaunchWorkspacePath } from "./project-resolver.js";
|
|
6
|
+
import { formatJoinLinkValidity, renderTerminalQr } from "./terminal-qr.js";
|
|
6
7
|
import { renderTuttiTerminalLogo } from "./terminal-logo.js";
|
|
7
8
|
import { runProviderSetupTui } from "./provider-tui.js";
|
|
8
9
|
import { LaunchError } from "./errors.js";
|
|
@@ -45,6 +46,14 @@ function renderCompletionPage(options) {
|
|
|
45
46
|
"Tutti is running in the background.",
|
|
46
47
|
"",
|
|
47
48
|
`Join URL: ${options.joinUrl}`,
|
|
49
|
+
formatJoinLinkValidity({
|
|
50
|
+
...(options.joinTokenExpiresAt === undefined
|
|
51
|
+
? {}
|
|
52
|
+
: { expiresAt: options.joinTokenExpiresAt }),
|
|
53
|
+
...(options.joinTokenReusable === undefined
|
|
54
|
+
? {}
|
|
55
|
+
: { reusable: options.joinTokenReusable }),
|
|
56
|
+
}),
|
|
48
57
|
"",
|
|
49
58
|
renderTerminalQr(options.joinUrl),
|
|
50
59
|
"",
|
|
@@ -64,8 +73,11 @@ async function ensureProviderConfigured(preparation) {
|
|
|
64
73
|
});
|
|
65
74
|
}
|
|
66
75
|
export async function runForegroundLaunchCommand(options) {
|
|
76
|
+
const workspacePath = resolveLaunchWorkspacePath({
|
|
77
|
+
...(options.target === undefined ? {} : { target: options.target }),
|
|
78
|
+
});
|
|
67
79
|
const result = await startLaunchProject({
|
|
68
|
-
workspacePath
|
|
80
|
+
workspacePath,
|
|
69
81
|
yes: options.yes,
|
|
70
82
|
confirm: confirmFromTty,
|
|
71
83
|
});
|
|
@@ -84,8 +96,11 @@ export async function runInternalHostRunCommand(options) {
|
|
|
84
96
|
}
|
|
85
97
|
}
|
|
86
98
|
export async function runBackgroundLaunchCommand(options) {
|
|
99
|
+
const workspacePath = resolveLaunchWorkspacePath({
|
|
100
|
+
...(options.target === undefined ? {} : { target: options.target }),
|
|
101
|
+
});
|
|
87
102
|
const preparation = await prepareLaunchProject({
|
|
88
|
-
workspacePath
|
|
103
|
+
workspacePath,
|
|
89
104
|
yes: options.yes,
|
|
90
105
|
confirm: confirmFromTty,
|
|
91
106
|
});
|
|
@@ -103,6 +118,12 @@ export async function runBackgroundLaunchCommand(options) {
|
|
|
103
118
|
}
|
|
104
119
|
process.stdout.write(`${renderCompletionPage({
|
|
105
120
|
joinUrl: ready.join_url,
|
|
121
|
+
...(ready.join_token_expires_at === undefined
|
|
122
|
+
? {}
|
|
123
|
+
: { joinTokenExpiresAt: ready.join_token_expires_at }),
|
|
124
|
+
...(ready.join_token_reusable === undefined
|
|
125
|
+
? {}
|
|
126
|
+
: { joinTokenReusable: ready.join_token_reusable }),
|
|
106
127
|
})}\n`);
|
|
107
128
|
await waitForEnter();
|
|
108
129
|
}
|
|
@@ -4,6 +4,8 @@ import type { ProjectId } from "@tutti/shared/ids";
|
|
|
4
4
|
export type ManagedHostReadyResult = {
|
|
5
5
|
endpoint: MachineRuntimeEndpointRecord;
|
|
6
6
|
join_url?: string;
|
|
7
|
+
join_token_expires_at?: string;
|
|
8
|
+
join_token_reusable?: boolean;
|
|
7
9
|
};
|
|
8
10
|
export declare function spawnDetachedHost(options: {
|
|
9
11
|
workspaceRoot: string;
|
|
@@ -74,6 +74,12 @@ export async function waitForManagedHostReady(options) {
|
|
|
74
74
|
return {
|
|
75
75
|
endpoint,
|
|
76
76
|
join_url: status.relay.join_url,
|
|
77
|
+
...(status.relay.join_token_expires_at === undefined
|
|
78
|
+
? {}
|
|
79
|
+
: { join_token_expires_at: status.relay.join_token_expires_at }),
|
|
80
|
+
...(status.relay.join_token_reusable === undefined
|
|
81
|
+
? {}
|
|
82
|
+
: { join_token_reusable: status.relay.join_token_reusable }),
|
|
77
83
|
};
|
|
78
84
|
}
|
|
79
85
|
lastReason = "host is running but Relay invite is not visible yet";
|
|
@@ -11,7 +11,13 @@ export type ExistingProjectContext = {
|
|
|
11
11
|
provider_config: OpenAiProviderConfigProjection;
|
|
12
12
|
provider_base_url?: string;
|
|
13
13
|
};
|
|
14
|
+
export declare function resolveLaunchWorkspacePath(options: {
|
|
15
|
+
target?: string;
|
|
16
|
+
cwd?: string;
|
|
17
|
+
env?: NodeJS.ProcessEnv;
|
|
18
|
+
}): string;
|
|
14
19
|
export declare function resolveExistingProjectContext(options: {
|
|
20
|
+
target?: string;
|
|
15
21
|
workspacePath?: string;
|
|
16
22
|
cwd?: string;
|
|
17
23
|
env?: NodeJS.ProcessEnv;
|