@xfey/tutti 0.1.10 → 0.1.12
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 +27 -27
- package/dist/server-shell/cli/errors.d.ts +2 -1
- package/dist/server-shell/cli/errors.js +55 -12
- 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 +25 -11
- package/dist/server-shell/cli/launch.js +0 -3
- package/dist/server-shell/cli/managed-host.d.ts +2 -0
- package/dist/server-shell/cli/managed-host.js +9 -18
- 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/provider-tui.d.ts +19 -0
- package/dist/server-shell/cli/provider-tui.js +64 -38
- package/dist/server-shell/cli/runtime-commands.d.ts +5 -5
- package/dist/server-shell/cli/runtime-commands.js +68 -25
- 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 +1 -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
|
}
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
|
-
import { formatCliError } from "./errors.js";
|
|
2
|
+
import { formatCliError, formatCliErrorReason } from "./errors.js";
|
|
3
3
|
import { parseCliArgs } from "./args.js";
|
|
4
4
|
import { runBackgroundLaunchCommand, runForegroundLaunchCommand, runInternalHostRunCommand, } from "./launch-command.js";
|
|
5
5
|
import { resolveExistingProjectContext } from "./project-resolver.js";
|
|
@@ -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,
|
|
@@ -102,20 +102,19 @@ async function runProviderSetupCommand(workspacePath) {
|
|
|
102
102
|
process.stdout.write([
|
|
103
103
|
"",
|
|
104
104
|
"Provider configured.",
|
|
105
|
-
`Base URL: ${result.base_url}`,
|
|
106
105
|
`Model: ${result.default_model}`,
|
|
107
106
|
`Key: ${result.redacted_key}`,
|
|
108
107
|
`Validated: ${result.validated_at}`,
|
|
109
108
|
].join("\n"));
|
|
110
109
|
process.stdout.write("\n");
|
|
111
110
|
}
|
|
112
|
-
function runDoctorCommand(
|
|
111
|
+
function runDoctorCommand(target) {
|
|
113
112
|
const checks = [];
|
|
114
113
|
checks.push(`Node: ${process.version}`);
|
|
115
114
|
checks.push(`Tutti: ${readCliVersion()}`);
|
|
116
115
|
try {
|
|
117
116
|
const project = resolveExistingProjectContext({
|
|
118
|
-
...(
|
|
117
|
+
...(target === undefined ? {} : { target }),
|
|
119
118
|
});
|
|
120
119
|
checks.push(`Project: ${project.display_name}`);
|
|
121
120
|
checks.push(`Project ID: ${project.project_id}`);
|
|
@@ -123,7 +122,8 @@ function runDoctorCommand(workspacePath) {
|
|
|
123
122
|
checks.push(`Host: ${project.runtime_endpoint === null ? "not running" : project.runtime_endpoint.base_url}`);
|
|
124
123
|
}
|
|
125
124
|
catch (error) {
|
|
126
|
-
checks.push(
|
|
125
|
+
checks.push("Project: unavailable");
|
|
126
|
+
checks.push(`Project detail: ${formatCliErrorReason(error)}`);
|
|
127
127
|
}
|
|
128
128
|
process.stdout.write(`${checks.join("\n")}\n`);
|
|
129
129
|
}
|
|
@@ -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" | "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;
|
|
@@ -7,4 +7,5 @@ export declare class LaunchError extends Error {
|
|
|
7
7
|
}
|
|
8
8
|
export declare function formatLaunchError(error: unknown): string;
|
|
9
9
|
export declare function formatCliError(error: unknown): string;
|
|
10
|
+
export declare function formatCliErrorReason(error: unknown): string;
|
|
10
11
|
//# sourceMappingURL=errors.d.ts.map
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { redactError, redactText } from "@tutti/shared/utils";
|
|
1
|
+
import { redactAndTruncateText, redactError, redactText } from "@tutti/shared/utils";
|
|
2
2
|
export class LaunchError extends Error {
|
|
3
3
|
code;
|
|
4
4
|
next;
|
|
@@ -20,24 +20,22 @@ export function formatLaunchError(error) {
|
|
|
20
20
|
"Tutti launch failed",
|
|
21
21
|
"",
|
|
22
22
|
`Code: ${error.code}`,
|
|
23
|
-
`Reason: ${
|
|
23
|
+
`Reason: ${formatBoundedText(error.message)}`,
|
|
24
24
|
];
|
|
25
25
|
const detailLines = Object.entries(error.details)
|
|
26
26
|
.filter(([, value]) => value !== undefined)
|
|
27
|
-
.map(([key, value]) => `Detail ${key}: ${
|
|
27
|
+
.map(([key, value]) => `Detail ${key}: ${formatBoundedDetail(value)}`);
|
|
28
28
|
if (detailLines.length > 0) {
|
|
29
29
|
lines.push(...detailLines);
|
|
30
30
|
}
|
|
31
31
|
lines.push(`Next: ${redactText(error.next)}`);
|
|
32
32
|
return lines.join("\n");
|
|
33
33
|
}
|
|
34
|
-
const redactedError = redactError(error);
|
|
35
|
-
const reason = typeof redactedError === "string" ? redactedError : JSON.stringify(redactedError);
|
|
36
34
|
return [
|
|
37
35
|
"Tutti launch failed",
|
|
38
36
|
"",
|
|
39
37
|
"Code: internal_error",
|
|
40
|
-
`Reason: ${
|
|
38
|
+
`Reason: ${formatUnknownErrorReason(error)}`,
|
|
41
39
|
"Next: Retry after checking the local debug logs.",
|
|
42
40
|
].join("\n");
|
|
43
41
|
}
|
|
@@ -46,26 +44,71 @@ export function formatCliError(error) {
|
|
|
46
44
|
if (error.code === "provider_setup_cancelled") {
|
|
47
45
|
return ["Tutti command cancelled", "", `Reason: ${redactText(error.message)}`].join("\n");
|
|
48
46
|
}
|
|
49
|
-
const lines = ["Tutti command failed", "", `Code: ${error.code}`, `Reason: ${
|
|
47
|
+
const lines = ["Tutti command failed", "", `Code: ${error.code}`, `Reason: ${formatBoundedText(error.message)}`];
|
|
50
48
|
const detailLines = Object.entries(error.details)
|
|
51
49
|
.filter(([, value]) => value !== undefined)
|
|
52
|
-
.map(([key, value]) => `Detail ${key}: ${
|
|
50
|
+
.map(([key, value]) => `Detail ${key}: ${formatBoundedDetail(value)}`);
|
|
53
51
|
if (detailLines.length > 0) {
|
|
54
52
|
lines.push(...detailLines);
|
|
55
53
|
}
|
|
56
54
|
lines.push(`Next: ${redactText(error.next)}`);
|
|
57
55
|
return lines.join("\n");
|
|
58
56
|
}
|
|
59
|
-
const redactedError = redactError(error);
|
|
60
|
-
const reason = typeof redactedError === "string" ? redactedError : JSON.stringify(redactedError);
|
|
61
57
|
return [
|
|
62
58
|
"Tutti command failed",
|
|
63
59
|
"",
|
|
64
60
|
"Code: internal_error",
|
|
65
|
-
`Reason: ${
|
|
61
|
+
`Reason: ${formatUnknownErrorReason(error)}`,
|
|
66
62
|
"Next: Retry after checking the local debug logs.",
|
|
67
63
|
].join("\n");
|
|
68
64
|
}
|
|
65
|
+
const MAX_REASON_LENGTH = 320;
|
|
66
|
+
const MAX_DETAIL_LENGTH = 720;
|
|
67
|
+
function formatBoundedText(value) {
|
|
68
|
+
return redactAndTruncateText(value, MAX_REASON_LENGTH);
|
|
69
|
+
}
|
|
70
|
+
function formatBoundedDetail(value) {
|
|
71
|
+
return redactAndTruncateText(formatDetailValue(value), MAX_DETAIL_LENGTH);
|
|
72
|
+
}
|
|
73
|
+
function isRecord(value) {
|
|
74
|
+
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
75
|
+
}
|
|
76
|
+
export function formatCliErrorReason(error) {
|
|
77
|
+
const redactedError = redactError(error);
|
|
78
|
+
if (typeof redactedError === "string" && redactedError.trim() !== "") {
|
|
79
|
+
return formatBoundedText(redactedError);
|
|
80
|
+
}
|
|
81
|
+
if (isRecord(redactedError)) {
|
|
82
|
+
const message = redactedError.message;
|
|
83
|
+
if (typeof message === "string" && message.trim() !== "") {
|
|
84
|
+
return formatBoundedText(message);
|
|
85
|
+
}
|
|
86
|
+
const name = redactedError.name;
|
|
87
|
+
if (typeof name === "string" && name.trim() !== "") {
|
|
88
|
+
return formatBoundedText(name);
|
|
89
|
+
}
|
|
90
|
+
}
|
|
91
|
+
return "Unexpected error.";
|
|
92
|
+
}
|
|
93
|
+
function formatUnknownErrorReason(error) {
|
|
94
|
+
return formatCliErrorReason(error);
|
|
95
|
+
}
|
|
96
|
+
function stripStackFields(value) {
|
|
97
|
+
if (Array.isArray(value)) {
|
|
98
|
+
return value.map((entry) => stripStackFields(entry));
|
|
99
|
+
}
|
|
100
|
+
if (isRecord(value)) {
|
|
101
|
+
const stripped = {};
|
|
102
|
+
for (const [key, nested] of Object.entries(value)) {
|
|
103
|
+
if (key.toLowerCase() === "stack") {
|
|
104
|
+
continue;
|
|
105
|
+
}
|
|
106
|
+
stripped[key] = stripStackFields(nested);
|
|
107
|
+
}
|
|
108
|
+
return stripped;
|
|
109
|
+
}
|
|
110
|
+
return value;
|
|
111
|
+
}
|
|
69
112
|
function formatDetailValue(value) {
|
|
70
113
|
if (Array.isArray(value)) {
|
|
71
114
|
return value.map((entry) => formatDetailValue(entry)).join(", ");
|
|
@@ -76,6 +119,6 @@ function formatDetailValue(value) {
|
|
|
76
119
|
if (typeof value === "string" || typeof value === "number" || typeof value === "boolean") {
|
|
77
120
|
return String(value);
|
|
78
121
|
}
|
|
79
|
-
return JSON.stringify(value) ?? "undefined";
|
|
122
|
+
return JSON.stringify(stripStackFields(value)) ?? "undefined";
|
|
80
123
|
}
|
|
81
124
|
//# sourceMappingURL=errors.js.map
|
|
@@ -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
|