@xfey/tutti 0.1.6 → 0.1.8

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.
@@ -0,0 +1,30 @@
1
+ import type { HostLocalLaunchStatus, HostLocalProjectProjection, HostLocalProviderConfigBody } from "../http/routes/local-control.js";
2
+ import type { HostProviderConfigProjection } from "../http/routes/project-api/types.js";
3
+ import type { MachineRuntimeEndpointRecord } from "./machine-local.js";
4
+ export type LocalControlFetch = (input: string | URL, init?: RequestInit) => Promise<Response>;
5
+ export declare function readHostLocalLaunchStatus(options: {
6
+ endpoint: MachineRuntimeEndpointRecord;
7
+ fetchImpl?: LocalControlFetch;
8
+ }): Promise<HostLocalLaunchStatus>;
9
+ export declare function rotateHostLocalInvite(options: {
10
+ endpoint: MachineRuntimeEndpointRecord;
11
+ fetchImpl?: LocalControlFetch;
12
+ }): Promise<HostLocalLaunchStatus>;
13
+ export declare function readHostLocalProject(options: {
14
+ endpoint: MachineRuntimeEndpointRecord;
15
+ fetchImpl?: LocalControlFetch;
16
+ }): Promise<HostLocalProjectProjection>;
17
+ export declare function readHostLocalProviderConfig(options: {
18
+ endpoint: MachineRuntimeEndpointRecord;
19
+ fetchImpl?: LocalControlFetch;
20
+ }): Promise<HostProviderConfigProjection>;
21
+ export declare function writeHostLocalProviderConfig(options: {
22
+ endpoint: MachineRuntimeEndpointRecord;
23
+ body: HostLocalProviderConfigBody;
24
+ fetchImpl?: LocalControlFetch;
25
+ }): Promise<HostProviderConfigProjection>;
26
+ export declare function requestHostLocalShutdown(options: {
27
+ endpoint: MachineRuntimeEndpointRecord;
28
+ fetchImpl?: LocalControlFetch;
29
+ }): Promise<void>;
30
+ //# sourceMappingURL=local-control-client.d.ts.map
@@ -0,0 +1,73 @@
1
+ const DEFAULT_TIMEOUT_MS = 2_000;
2
+ async function fetchWithTimeout(fetchImpl, url, init = {}, timeoutMs = DEFAULT_TIMEOUT_MS) {
3
+ const controller = new AbortController();
4
+ const timeout = setTimeout(() => {
5
+ controller.abort();
6
+ }, timeoutMs);
7
+ try {
8
+ return await fetchImpl(url, {
9
+ ...init,
10
+ signal: controller.signal,
11
+ });
12
+ }
13
+ finally {
14
+ clearTimeout(timeout);
15
+ }
16
+ }
17
+ async function requestLocalJson(options) {
18
+ const response = await fetchWithTimeout(options.fetchImpl ?? fetch, new URL(options.path, options.endpoint.base_url), {
19
+ method: options.method ?? "GET",
20
+ headers: {
21
+ accept: "application/json",
22
+ authorization: `Bearer ${options.endpoint.token}`,
23
+ ...(options.body === undefined ? {} : { "content-type": "application/json" }),
24
+ },
25
+ ...(options.body === undefined ? {} : { body: JSON.stringify(options.body) }),
26
+ }, options.timeoutMs);
27
+ if (!response.ok) {
28
+ throw new Error(`host-local ${options.path} returned HTTP ${response.status}`);
29
+ }
30
+ return (await response.json());
31
+ }
32
+ export async function readHostLocalLaunchStatus(options) {
33
+ return await requestLocalJson({
34
+ ...options,
35
+ path: "/host-local/v1/launch-status",
36
+ });
37
+ }
38
+ export async function rotateHostLocalInvite(options) {
39
+ return await requestLocalJson({
40
+ ...options,
41
+ method: "POST",
42
+ path: "/host-local/v1/invite/rotate",
43
+ });
44
+ }
45
+ export async function readHostLocalProject(options) {
46
+ return await requestLocalJson({
47
+ ...options,
48
+ path: "/host-local/v1/project",
49
+ });
50
+ }
51
+ export async function readHostLocalProviderConfig(options) {
52
+ return await requestLocalJson({
53
+ ...options,
54
+ path: "/host-local/v1/provider/config",
55
+ });
56
+ }
57
+ export async function writeHostLocalProviderConfig(options) {
58
+ return await requestLocalJson({
59
+ endpoint: options.endpoint,
60
+ ...(options.fetchImpl === undefined ? {} : { fetchImpl: options.fetchImpl }),
61
+ method: "POST",
62
+ path: "/host-local/v1/provider/config",
63
+ body: options.body,
64
+ });
65
+ }
66
+ export async function requestHostLocalShutdown(options) {
67
+ await requestLocalJson({
68
+ ...options,
69
+ method: "POST",
70
+ path: "/host-local/v1/shutdown",
71
+ });
72
+ }
73
+ //# sourceMappingURL=local-control-client.js.map
@@ -0,0 +1,19 @@
1
+ import { type FetchLike } from "./host-runtime-endpoint.js";
2
+ import { type MachineRuntimeEndpointRecord } from "./machine-local.js";
3
+ import type { ProjectId } from "@tutti/shared/ids";
4
+ export type ManagedHostReadyResult = {
5
+ endpoint: MachineRuntimeEndpointRecord;
6
+ join_url?: string;
7
+ };
8
+ export declare function spawnDetachedHost(options: {
9
+ workspaceRoot: string;
10
+ env?: NodeJS.ProcessEnv;
11
+ }): void;
12
+ export declare function waitForManagedHostReady(options: {
13
+ tuttiHome: string;
14
+ projectId: ProjectId;
15
+ workspaceRoot: string;
16
+ fetchImpl?: FetchLike;
17
+ timeoutMs?: number;
18
+ }): Promise<ManagedHostReadyResult>;
19
+ //# sourceMappingURL=managed-host.d.ts.map
@@ -0,0 +1,103 @@
1
+ import { spawn } from "node:child_process";
2
+ import { readFileSync } from "node:fs";
3
+ import { resolve } from "node:path";
4
+ import { createRuntimeEndpointProbe } from "./host-runtime-endpoint.js";
5
+ import { getHostLogFilePath, readMachineRuntimeEndpoint, } from "./machine-local.js";
6
+ import { readHostLocalLaunchStatus, rotateHostLocalInvite } from "./local-control-client.js";
7
+ const DEFAULT_READY_TIMEOUT_MS = 60_000;
8
+ const READY_POLL_INTERVAL_MS = 250;
9
+ function delay(ms) {
10
+ return new Promise((resolveDelay) => {
11
+ setTimeout(resolveDelay, ms);
12
+ });
13
+ }
14
+ function currentCliEntrypoint() {
15
+ const entrypoint = process.argv[1];
16
+ if (entrypoint === undefined || entrypoint.trim() === "") {
17
+ throw new Error("Cannot locate the current Tutti CLI entrypoint.");
18
+ }
19
+ return entrypoint;
20
+ }
21
+ export function spawnDetachedHost(options) {
22
+ const child = spawn(process.execPath, [
23
+ ...process.execArgv,
24
+ currentCliEntrypoint(),
25
+ "internal",
26
+ "host-run",
27
+ "--workspace",
28
+ resolve(options.workspaceRoot),
29
+ "--yes",
30
+ ], {
31
+ cwd: options.workspaceRoot,
32
+ detached: true,
33
+ env: {
34
+ ...process.env,
35
+ ...(options.env ?? {}),
36
+ },
37
+ stdio: "ignore",
38
+ });
39
+ child.unref();
40
+ }
41
+ function readHostLogTail(tuttiHome, maxLines = 80) {
42
+ try {
43
+ const text = readFileSync(getHostLogFilePath(tuttiHome), "utf8");
44
+ return text.split(/\r?\n/u).filter(Boolean).slice(-maxLines).join("\n");
45
+ }
46
+ catch {
47
+ return "";
48
+ }
49
+ }
50
+ export async function waitForManagedHostReady(options) {
51
+ const deadline = Date.now() + (options.timeoutMs ?? DEFAULT_READY_TIMEOUT_MS);
52
+ const probe = createRuntimeEndpointProbe(options.fetchImpl ?? fetch);
53
+ let lastReason = "runtime endpoint was not written";
54
+ while (Date.now() < deadline) {
55
+ const endpoint = readMachineRuntimeEndpoint(options.tuttiHome, options.projectId);
56
+ if (endpoint === null) {
57
+ await delay(READY_POLL_INTERVAL_MS);
58
+ continue;
59
+ }
60
+ if (resolve(endpoint.workspace_root) !== resolve(options.workspaceRoot)) {
61
+ lastReason = "runtime endpoint points at a different workspace";
62
+ await delay(READY_POLL_INTERVAL_MS);
63
+ continue;
64
+ }
65
+ const health = await probe(endpoint);
66
+ if (health.kind === "stale") {
67
+ lastReason = health.reason;
68
+ await delay(READY_POLL_INTERVAL_MS);
69
+ continue;
70
+ }
71
+ try {
72
+ let status = await readHostLocalLaunchStatus({
73
+ endpoint,
74
+ ...(options.fetchImpl === undefined ? {} : { fetchImpl: options.fetchImpl }),
75
+ });
76
+ if (status.relay?.join_url === undefined) {
77
+ status = await rotateHostLocalInvite({
78
+ endpoint,
79
+ ...(options.fetchImpl === undefined ? {} : { fetchImpl: options.fetchImpl }),
80
+ });
81
+ }
82
+ if (status.relay?.join_url !== undefined) {
83
+ return {
84
+ endpoint,
85
+ join_url: status.relay.join_url,
86
+ };
87
+ }
88
+ lastReason = "host is running but Relay invite is not visible yet";
89
+ }
90
+ catch (error) {
91
+ lastReason = error instanceof Error ? error.message : "host-local launch status failed";
92
+ }
93
+ await delay(READY_POLL_INTERVAL_MS);
94
+ }
95
+ const tail = readHostLogTail(options.tuttiHome);
96
+ throw new Error([
97
+ `Tutti host did not become ready before timeout: ${lastReason}`,
98
+ tail.length === 0 ? "" : `Recent host log:\n${tail}`,
99
+ ]
100
+ .filter(Boolean)
101
+ .join("\n"));
102
+ }
103
+ //# sourceMappingURL=managed-host.js.map
@@ -0,0 +1,19 @@
1
+ import { type ProjectId } from "@tutti/shared/ids";
2
+ import { type OpenAiProviderConfigProjection } from "../../providers/openai/index.js";
3
+ import { type MachineProjectBindingRecord, type MachineRuntimeEndpointRecord } from "./machine-local.js";
4
+ export type ExistingProjectContext = {
5
+ workspace_root: string;
6
+ tutti_home: string;
7
+ project_id: ProjectId;
8
+ display_name: string;
9
+ binding: MachineProjectBindingRecord | null;
10
+ runtime_endpoint: MachineRuntimeEndpointRecord | null;
11
+ provider_config: OpenAiProviderConfigProjection;
12
+ provider_base_url?: string;
13
+ };
14
+ export declare function resolveExistingProjectContext(options: {
15
+ workspacePath?: string;
16
+ cwd?: string;
17
+ env?: NodeJS.ProcessEnv;
18
+ }): ExistingProjectContext;
19
+ //# sourceMappingURL=project-resolver.d.ts.map
@@ -0,0 +1,35 @@
1
+ import { resolve } from "node:path";
2
+ import { readOpenAiProviderConfigProjection, readProjectOpenAiProviderConfig, resolveTuttiHome, } from "../../providers/openai/index.js";
3
+ import { LaunchError } from "./errors.js";
4
+ import { assertSafeProjectRoot } from "./git-bootstrap.js";
5
+ import { readMachineProjectBinding, readMachineRuntimeEndpoint, } from "./machine-local.js";
6
+ import { readProjectDisplayName, readProjectIdentity } from "./project-identity.js";
7
+ export function resolveExistingProjectContext(options) {
8
+ const cwd = options.cwd ?? process.cwd();
9
+ const env = options.env ?? process.env;
10
+ const workspaceRoot = resolve(cwd, options.workspacePath ?? ".");
11
+ assertSafeProjectRoot(workspaceRoot);
12
+ const identity = readProjectIdentity(workspaceRoot);
13
+ if (identity.kind !== "present") {
14
+ throw new LaunchError("project_identity_conflict", "Current directory is not a Tutti project", "Run `tutti launch` from a project root first.");
15
+ }
16
+ const tuttiHome = resolveTuttiHome(env.TUTTI_HOME, cwd);
17
+ const binding = readMachineProjectBinding(tuttiHome, identity.project_id);
18
+ const projectConfig = readProjectOpenAiProviderConfig(tuttiHome, identity.project_id);
19
+ return {
20
+ workspace_root: workspaceRoot,
21
+ tutti_home: tuttiHome,
22
+ project_id: identity.project_id,
23
+ display_name: readProjectDisplayName(workspaceRoot) ?? identity.project_id,
24
+ binding,
25
+ runtime_endpoint: readMachineRuntimeEndpoint(tuttiHome, identity.project_id),
26
+ provider_config: readOpenAiProviderConfigProjection({
27
+ tuttiHome,
28
+ projectId: identity.project_id,
29
+ }),
30
+ ...(projectConfig.kind === "ok" && projectConfig.config.api_base_url !== undefined
31
+ ? { provider_base_url: projectConfig.config.api_base_url }
32
+ : {}),
33
+ };
34
+ }
35
+ //# sourceMappingURL=project-resolver.js.map
@@ -0,0 +1,16 @@
1
+ import type { ProjectId } from "@tutti/shared/ids";
2
+ export type ProviderSetupResult = {
3
+ base_url: string;
4
+ redacted_key: string;
5
+ default_model: string;
6
+ validated_at: string;
7
+ };
8
+ export declare function runProviderSetupTui(options: {
9
+ projectName: string;
10
+ tuttiHome: string;
11
+ projectId: ProjectId;
12
+ initialBaseUrl?: string;
13
+ stdin?: NodeJS.ReadStream;
14
+ stdout?: NodeJS.WriteStream;
15
+ }): Promise<ProviderSetupResult>;
16
+ //# sourceMappingURL=provider-tui.d.ts.map
@@ -0,0 +1,253 @@
1
+ import { emitKeypressEvents } from "node:readline";
2
+ import { redactError } from "@tutti/shared/utils";
3
+ import { configureProjectOpenAiProvider, DEFAULT_OPENAI_MODEL, } from "../../providers/openai/index.js";
4
+ import { renderTuttiTerminalLogo } from "./terminal-logo.js";
5
+ const DEFAULT_BASE_URL = "https://api.openai.com/v1";
6
+ const HIDE_CURSOR = "\u001B[?25l";
7
+ const SHOW_CURSOR = "\u001B[?25h";
8
+ const CLEAR = "\u001B[2J\u001B[H";
9
+ function fieldLine(options) {
10
+ const marker = options.active ? ">" : " ";
11
+ const value = options.secret && options.value.length > 0 ? "*".repeat(options.value.length) : options.value;
12
+ return `${marker} ${options.label.padEnd(10)} ${value}${options.active ? " _" : ""}`;
13
+ }
14
+ function renderProviderForm(options) {
15
+ const instruction = options.state.step === "base-url"
16
+ ? "Enter the provider base URL, then press Enter."
17
+ : "Enter the API key, then press Enter to validate. Press Esc to edit the base URL.";
18
+ const lines = [
19
+ CLEAR,
20
+ HIDE_CURSOR,
21
+ renderTuttiTerminalLogo({ tty: options.tty === true }),
22
+ "",
23
+ "Provider setup",
24
+ "",
25
+ `Project: ${options.projectName}`,
26
+ `${instruction} Press Ctrl-C to cancel.`,
27
+ "",
28
+ fieldLine({
29
+ active: options.state.step === "base-url",
30
+ label: "Base URL",
31
+ value: options.state.baseUrl,
32
+ }),
33
+ fieldLine({
34
+ active: options.state.step === "api-key",
35
+ label: "API Key",
36
+ value: options.state.apiKey,
37
+ secret: true,
38
+ }),
39
+ "",
40
+ ];
41
+ if (options.state.error !== undefined) {
42
+ lines.push(`Error: ${options.state.error}`, "");
43
+ }
44
+ if (options.footer !== undefined) {
45
+ lines.push(options.footer);
46
+ }
47
+ return lines.join("\n");
48
+ }
49
+ function validationFailureMessage(result) {
50
+ if (result.kind === "configured") {
51
+ return "";
52
+ }
53
+ const retry = result.retryable ? " Retry after the provider recovers." : "";
54
+ return `${result.reason}.${retry}`;
55
+ }
56
+ function waitForKeypress(stdin) {
57
+ return new Promise((resolve, reject) => {
58
+ const onKeypress = (_character, key) => {
59
+ cleanup();
60
+ if (key.ctrl === true && key.name === "c") {
61
+ reject(new Error("Provider setup cancelled"));
62
+ return;
63
+ }
64
+ resolve();
65
+ };
66
+ const cleanup = () => {
67
+ stdin.off("keypress", onKeypress);
68
+ if (stdin.isTTY) {
69
+ stdin.setRawMode(false);
70
+ }
71
+ };
72
+ if (stdin.isTTY) {
73
+ stdin.setRawMode(true);
74
+ }
75
+ stdin.once("keypress", onKeypress);
76
+ });
77
+ }
78
+ async function readProviderForm(options) {
79
+ const state = {
80
+ step: options.initialStep ?? "base-url",
81
+ baseUrl: options.initialBaseUrl ?? DEFAULT_BASE_URL,
82
+ apiKey: options.initialApiKey ?? "",
83
+ error: undefined,
84
+ };
85
+ return await new Promise((resolve, reject) => {
86
+ const render = () => {
87
+ options.stdout.write(renderProviderForm({
88
+ projectName: options.projectName,
89
+ state,
90
+ tty: options.stdout.isTTY === true,
91
+ }));
92
+ };
93
+ const cleanup = () => {
94
+ options.stdin.off("keypress", onKeypress);
95
+ if (options.stdin.isTTY) {
96
+ options.stdin.setRawMode(false);
97
+ }
98
+ options.stdout.write(SHOW_CURSOR);
99
+ };
100
+ const onKeypress = (character, key) => {
101
+ if (key.ctrl === true && key.name === "c") {
102
+ cleanup();
103
+ reject(new Error("Provider setup cancelled"));
104
+ return;
105
+ }
106
+ if (key.name === "escape") {
107
+ if (state.step === "api-key") {
108
+ state.step = "base-url";
109
+ state.error = undefined;
110
+ render();
111
+ return;
112
+ }
113
+ cleanup();
114
+ reject(new Error("Provider setup cancelled"));
115
+ return;
116
+ }
117
+ if (key.name === "tab") {
118
+ state.step = state.step === "base-url" ? "api-key" : "base-url";
119
+ state.error = undefined;
120
+ render();
121
+ return;
122
+ }
123
+ if (key.name === "return") {
124
+ if (state.step === "base-url") {
125
+ if (state.baseUrl.trim() === "") {
126
+ state.error = "Base URL is required.";
127
+ render();
128
+ return;
129
+ }
130
+ state.step = "api-key";
131
+ state.error = undefined;
132
+ render();
133
+ return;
134
+ }
135
+ if (state.apiKey.trim() === "") {
136
+ state.error = "API key is required.";
137
+ render();
138
+ return;
139
+ }
140
+ cleanup();
141
+ resolve({ baseUrl: state.baseUrl.trim(), apiKey: state.apiKey.trim() });
142
+ return;
143
+ }
144
+ if (key.name === "backspace") {
145
+ if (state.step === "base-url") {
146
+ state.baseUrl = state.baseUrl.slice(0, -1);
147
+ }
148
+ else {
149
+ state.apiKey = state.apiKey.slice(0, -1);
150
+ }
151
+ state.error = undefined;
152
+ render();
153
+ return;
154
+ }
155
+ if (character !== undefined && character >= " " && character !== "\u007F") {
156
+ if (state.step === "base-url") {
157
+ state.baseUrl += character;
158
+ }
159
+ else {
160
+ state.apiKey += character;
161
+ }
162
+ state.error = undefined;
163
+ render();
164
+ }
165
+ };
166
+ emitKeypressEvents(options.stdin);
167
+ if (options.stdin.isTTY) {
168
+ options.stdin.setRawMode(true);
169
+ }
170
+ options.stdin.on("keypress", onKeypress);
171
+ render();
172
+ });
173
+ }
174
+ function renderChecking(projectName, frame, tty) {
175
+ const frames = ["|", "/", "-", "\\"];
176
+ const indicator = frames[frame % frames.length] ?? "|";
177
+ return [
178
+ CLEAR,
179
+ HIDE_CURSOR,
180
+ renderTuttiTerminalLogo({ tty }),
181
+ "",
182
+ "Provider setup",
183
+ "",
184
+ `Project: ${projectName}`,
185
+ "",
186
+ `${indicator} Checking provider connection with ${DEFAULT_OPENAI_MODEL}...`,
187
+ ].join("\n");
188
+ }
189
+ export async function runProviderSetupTui(options) {
190
+ const stdin = options.stdin ?? process.stdin;
191
+ const stdout = options.stdout ?? process.stdout;
192
+ if (!stdin.isTTY || !stdout.isTTY) {
193
+ throw new Error("Provider setup requires an interactive terminal.");
194
+ }
195
+ let initialBaseUrl = options.initialBaseUrl;
196
+ let initialApiKey = "";
197
+ let initialStep = "base-url";
198
+ while (true) {
199
+ const input = await readProviderForm({
200
+ projectName: options.projectName,
201
+ ...(initialBaseUrl === undefined ? {} : { initialBaseUrl }),
202
+ ...(initialApiKey === "" ? {} : { initialApiKey }),
203
+ initialStep,
204
+ stdin,
205
+ stdout,
206
+ });
207
+ initialBaseUrl = input.baseUrl;
208
+ initialApiKey = input.apiKey;
209
+ initialStep = "api-key";
210
+ let frame = 0;
211
+ const interval = setInterval(() => {
212
+ stdout.write(renderChecking(options.projectName, frame, stdout.isTTY === true));
213
+ frame += 1;
214
+ }, 120);
215
+ try {
216
+ stdout.write(renderChecking(options.projectName, frame, stdout.isTTY === true));
217
+ const result = await configureProjectOpenAiProvider({
218
+ tuttiHome: options.tuttiHome,
219
+ projectId: options.projectId,
220
+ apiBaseUrl: input.baseUrl,
221
+ apiKey: input.apiKey,
222
+ });
223
+ clearInterval(interval);
224
+ stdout.write(SHOW_CURSOR);
225
+ if (result.kind === "configured") {
226
+ return {
227
+ base_url: input.baseUrl,
228
+ redacted_key: result.projection.redacted_key,
229
+ default_model: result.projection.default_model,
230
+ validated_at: result.projection.validated_at,
231
+ };
232
+ }
233
+ stdout.write(renderProviderForm({
234
+ projectName: options.projectName,
235
+ state: {
236
+ step: "api-key",
237
+ baseUrl: input.baseUrl,
238
+ apiKey: input.apiKey,
239
+ error: validationFailureMessage(result),
240
+ },
241
+ footer: "Press any key to edit and try again.",
242
+ tty: stdout.isTTY === true,
243
+ }));
244
+ await waitForKeypress(stdin);
245
+ }
246
+ catch (error) {
247
+ clearInterval(interval);
248
+ stdout.write(SHOW_CURSOR);
249
+ throw new Error(`Provider setup failed: ${String(redactError(error))}`, { cause: error });
250
+ }
251
+ }
252
+ }
253
+ //# sourceMappingURL=provider-tui.js.map
@@ -0,0 +1,32 @@
1
+ import { type ProjectId } from "@tutti/shared/ids";
2
+ import { type FetchLike } from "./host-runtime-endpoint.js";
3
+ import { type MachineRuntimeEndpointRecord } from "./machine-local.js";
4
+ export type RuntimeProjectRow = {
5
+ project_id: ProjectId;
6
+ status: "online" | "stale";
7
+ display_name: string;
8
+ workspace_root: string;
9
+ endpoint?: string;
10
+ provider_status?: string;
11
+ relay_project_ref?: string;
12
+ join_url?: string;
13
+ };
14
+ export declare function listRuntimeProjects(options?: {
15
+ env?: NodeJS.ProcessEnv;
16
+ cwd?: string;
17
+ fetchImpl?: FetchLike;
18
+ }): Promise<RuntimeProjectRow[]>;
19
+ export declare function runPsCommand(): Promise<string>;
20
+ export declare function runPsManageCommand(options?: {
21
+ stdin?: NodeJS.ReadStream;
22
+ stdout?: NodeJS.WriteStream;
23
+ }): Promise<void>;
24
+ export declare function runStopCommand(workspacePath?: string): Promise<string>;
25
+ export declare function runInviteCommand(workspacePath?: string): Promise<string>;
26
+ export declare function runProviderStatusCommand(workspacePath?: string): string;
27
+ export declare function runLogsCommand(workspacePath?: string, tailLines?: number): string;
28
+ export declare function readRuntimeEndpointForProject(tuttiHome: string, projectId: ProjectId): MachineRuntimeEndpointRecord | null;
29
+ export declare function runtimeEndpointPathForProject(tuttiHome: string, projectId: ProjectId): string;
30
+ export declare function projectLocalStoreRoot(tuttiHome: string, projectId: ProjectId): string;
31
+ export declare function resolveWorkspacePath(value: string | undefined): string;
32
+ //# sourceMappingURL=runtime-commands.d.ts.map