@xfey/tutti 0.1.5 → 0.1.7
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +4 -4
- package/dist/providers/openai/app-server/read-only-procedure.js +3 -5
- package/dist/providers/openai/app-server/sandbox-policy.d.ts +18 -0
- package/dist/providers/openai/app-server/sandbox-policy.js +16 -0
- package/dist/providers/openai/app-server/smoke.js +3 -3
- package/dist/providers/openai/app-server/workspace-write-run.js +3 -5
- package/dist/providers/openai/codex-app-server.d.ts +0 -9
- package/dist/providers/openai/codex-app-server.js +0 -5
- package/dist/server-shell/cli/args.d.ts +42 -0
- package/dist/server-shell/cli/args.js +185 -0
- package/dist/server-shell/cli/cli.js +179 -40
- package/dist/server-shell/cli/errors.d.ts +1 -0
- package/dist/server-shell/cli/errors.js +22 -0
- package/dist/server-shell/cli/host-server-runtime.js +8 -0
- package/dist/server-shell/cli/launch-command.d.ts +19 -0
- package/dist/server-shell/cli/launch-command.js +127 -0
- package/dist/server-shell/cli/local-control-client.d.ts +30 -0
- package/dist/server-shell/cli/local-control-client.js +73 -0
- package/dist/server-shell/cli/managed-host.d.ts +19 -0
- package/dist/server-shell/cli/managed-host.js +103 -0
- package/dist/server-shell/cli/project-resolver.d.ts +19 -0
- package/dist/server-shell/cli/project-resolver.js +35 -0
- package/dist/server-shell/cli/provider-tui.d.ts +16 -0
- package/dist/server-shell/cli/provider-tui.js +226 -0
- package/dist/server-shell/cli/runtime-commands.d.ts +32 -0
- package/dist/server-shell/cli/runtime-commands.js +308 -0
- package/dist/server-shell/cli/terminal-qr.d.ts +2 -0
- package/dist/server-shell/cli/terminal-qr.js +11 -0
- package/dist/server-shell/cli/version.d.ts +2 -0
- package/dist/server-shell/cli/version.js +7 -0
- package/dist/server-shell/http/routes/local-control.d.ts +1 -0
- package/dist/server-shell/http/routes/local-control.js +13 -0
- package/package.json +2 -1
- package/dist/providers/openai/app-server/permission-profile.d.ts +0 -64
- package/dist/providers/openai/app-server/permission-profile.js +0 -67
package/README.md
CHANGED
|
@@ -20,14 +20,14 @@
|
|
|
20
20
|
- `cli-package.mjs`:生成临时 staging `@xfey/tutti` package artifact 的脚本。
|
|
21
21
|
- `prepare-package-assets.mjs`:把 Web build output 和内部 runtime package 复制到 staging package 所需位置。
|
|
22
22
|
- `pack-dry-run.mjs`:构建 staging package 并执行 `npm pack --dry-run --json` 文件列表校验。
|
|
23
|
-
- `smoke-packed-cli.mjs`:安装并验证 packed CLI tarball 的本地 smoke。
|
|
23
|
+
- `smoke-packed-cli.mjs`:安装并验证 packed CLI tarball 的本地 smoke,包括 CLI help / version、runtime deps 和 QR dependency。
|
|
24
24
|
|
|
25
25
|
## 当前状态
|
|
26
26
|
|
|
27
|
-
- 已具备 Fastify host shell
|
|
27
|
+
- 已具备 Fastify host shell、后台 host manager CLI 套件、Dev Runner、Host Project API、SSE、SQLite store、collaboration-state、workspace read-state、project timeline observability helper、Control Plane、Procedure / Run、workspace-ops、checks、Relay staged upload finalize、user skills 文件服务、artifacts 可视化产物查看层和 OpenAI / Codex provider 接入。
|
|
28
28
|
- Host server 支持在显式 `webStatic.root` 或 packaged `web/` 存在时服务 Web 静态产物;`/api/*`、SSE、health 和 host-local control 不被 SPA fallback 吞掉。
|
|
29
29
|
- Root `pack:dry-run` 会先清理 build output、重新构建,再复制 `apps/web/dist` 到 package-local `web/`,生成临时 staging `@xfey/tutti` artifact,并校验 npm pack 文件列表包含 CLI、migrations、prompts、Web index、bundled `@tutti/shared` / `@tutti/relay-client` 以及它们的外部 runtime dependencies,同时排除源码、source map、日志、SQLite、`.env`、`.tutti-dev` 与测试产物。
|
|
30
|
-
- Root `smoke:packed-cli` 同样从干净 build output 生成 staging `@xfey/tutti` tarball、在临时目录安装,并验证 SQLite native binding、`tutti --help` / `tutti launch --help`、migrations、prompts、Web static index、bundled internal dependencies、bundled external runtime dependencies、仓库锁定的 `@openai/codex` binary 可执行性和临时全局安装路径;该 smoke 不需要 Tutti source repo 存在。
|
|
30
|
+
- Root `smoke:packed-cli` 同样从干净 build output 生成 staging `@xfey/tutti` tarball、在临时目录安装,并验证 SQLite native binding、`tutti --help` / `tutti launch --help` / `tutti --version`、migrations、prompts、Web static index、bundled internal dependencies、bundled external runtime dependencies、CLI QR dependency、仓库锁定的 `@openai/codex` binary 可执行性和临时全局安装路径;该 smoke 不需要 Tutti source repo 存在。
|
|
31
31
|
- `prompts/` 是所有模型-facing prompt、指令模板和 Codex 执行模板的统一落点;长 prompt 不内联在 TypeScript 中。
|
|
32
32
|
- Host Project API 只信任 Relay tunnel metadata 注入的 `relay_session_context`;direct loopback 缺少可信 context 时不作为浏览器工作区入口。
|
|
33
33
|
- 详细模块落点见 `src/README.md`;logger redaction、command idempotency、schema、provider 与 Codex 边界以 `docs/system/` 对应 canonical 文档为准。
|
|
@@ -43,7 +43,7 @@
|
|
|
43
43
|
- packaged / production `tutti launch` 默认连接 `https://tutti.now`;`TUTTI_RELAY_URL` 可覆盖 Relay URL,本地开发 dev runner 默认使用 `http://127.0.0.1:4370`。
|
|
44
44
|
- host 注册使用 machine-local `host_registration_secret`;该 secret 明文只可写入受限权限的 machine-local secret store,不写入目标项目 repo、Git local config 项目身份、SQLite 协作真相或日志,`binding.json` 只保存引用。
|
|
45
45
|
- Relay registration 成功后只把 `relay_project_ref` 写回 machine-local binding;join URL 只在 host 本机终端或当前 host-local control 内存状态中展示,不写入 binding、runtime endpoint、SQLite 协作真相或日志。
|
|
46
|
-
- 普通 `tutti launch`
|
|
46
|
+
- 普通 `tutti launch` 默认后台运行 host,CLI 完成页展示 join URL 和二维码;`tutti launch --foreground` 保留前台诊断模式。Host HTTP request log 与 Control Plane / Run Pipeline runtime log 写入 `TUTTI_HOME/logs/host.log`,不写入目标项目 repo 或常规 CLI stdout。
|
|
47
47
|
- Reference 和 Skills 上传的最终写入由 Host Project API 完成;Host 只通过 Relay host-control resolve 获取短期 R2 URL 下载 staged object,不接收浏览器 base64 文件正文,也不持久化 presigned URL。
|
|
48
48
|
- Host Project API command 的 browser session context 只信任 Relay tunnel metadata 中的 `relay_session_context`;route handler 不读取浏览器身份 header,也不接受 payload 内身份字段。
|
|
49
49
|
- Fastify request log、debug log、SSE payload、activity event 和 run result 都必须经过 redaction,不得泄露 provider secret、host registration secret、host connection token、join token、cookie、host 绝对路径或 run workspace path。
|
|
@@ -5,7 +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 {
|
|
8
|
+
import { createReadOnlySandboxPolicy } from "./sandbox-policy.js";
|
|
9
9
|
import { buildCodexAppServerProviderConfigOverrides, resolveCodexAppServerModelProvider, } from "./provider-config.js";
|
|
10
10
|
import { buildCodexAppServerTurnInput, resolveCodexAppServerContextRuntimeHint, resolveCodexAppServerSkillInputs, CodexAppServerSkillSelectionError, } from "./skills.js";
|
|
11
11
|
import { containsKnownSecret, createCodexSpawnEnv, extractAgentText, extractServerRequestMethod, findJsonObjectCandidates, isApiKeyAccount, readThreadId, readTurnError, readTurnStatus, redactWithKnownValues, compactText, } from "./runtime-helpers.js";
|
|
@@ -166,9 +166,7 @@ export async function runCodexAppServerReadOnlyProcedure(options) {
|
|
|
166
166
|
model,
|
|
167
167
|
modelProvider,
|
|
168
168
|
approvalPolicy: "never",
|
|
169
|
-
|
|
170
|
-
networkEnabled,
|
|
171
|
-
}),
|
|
169
|
+
sandbox: "read-only",
|
|
172
170
|
config: buildReadOnlyThreadConfig(options),
|
|
173
171
|
ephemeral: true,
|
|
174
172
|
experimentalRawEvents: false,
|
|
@@ -189,7 +187,7 @@ export async function runCodexAppServerReadOnlyProcedure(options) {
|
|
|
189
187
|
}),
|
|
190
188
|
cwd: appServerCwd,
|
|
191
189
|
approvalPolicy: "never",
|
|
192
|
-
|
|
190
|
+
sandboxPolicy: createReadOnlySandboxPolicy({
|
|
193
191
|
networkEnabled,
|
|
194
192
|
}),
|
|
195
193
|
model,
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
export type ReadOnlySandboxPolicyOptions = {
|
|
2
|
+
networkEnabled?: boolean;
|
|
3
|
+
};
|
|
4
|
+
export type WorkspaceWriteSandboxPolicyOptions = {
|
|
5
|
+
networkEnabled?: boolean;
|
|
6
|
+
};
|
|
7
|
+
export declare function createReadOnlySandboxPolicy(options?: ReadOnlySandboxPolicyOptions): {
|
|
8
|
+
type: "readOnly";
|
|
9
|
+
networkAccess: boolean;
|
|
10
|
+
};
|
|
11
|
+
export declare function createWorkspaceWriteSandboxPolicy(writableRoot: string, options?: WorkspaceWriteSandboxPolicyOptions): {
|
|
12
|
+
type: "workspaceWrite";
|
|
13
|
+
writableRoots: string[];
|
|
14
|
+
networkAccess: boolean;
|
|
15
|
+
excludeTmpdirEnvVar: boolean;
|
|
16
|
+
excludeSlashTmp: boolean;
|
|
17
|
+
};
|
|
18
|
+
//# sourceMappingURL=sandbox-policy.d.ts.map
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
export function createReadOnlySandboxPolicy(options = {}) {
|
|
2
|
+
return {
|
|
3
|
+
type: "readOnly",
|
|
4
|
+
networkAccess: options.networkEnabled ?? true,
|
|
5
|
+
};
|
|
6
|
+
}
|
|
7
|
+
export function createWorkspaceWriteSandboxPolicy(writableRoot, options = {}) {
|
|
8
|
+
return {
|
|
9
|
+
type: "workspaceWrite",
|
|
10
|
+
writableRoots: [writableRoot],
|
|
11
|
+
networkAccess: options.networkEnabled ?? true,
|
|
12
|
+
excludeTmpdirEnvVar: false,
|
|
13
|
+
excludeSlashTmp: false,
|
|
14
|
+
};
|
|
15
|
+
}
|
|
16
|
+
//# sourceMappingURL=sandbox-policy.js.map
|
|
@@ -5,7 +5,7 @@ import { join } from "node:path";
|
|
|
5
5
|
import { CODEX_APP_SERVER_COMMAND, CODEX_APP_SERVER_LISTEN_URL, 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 {
|
|
8
|
+
import { createReadOnlySandboxPolicy } from "./sandbox-policy.js";
|
|
9
9
|
import { buildCodexAppServerProviderConfigOverrides, resolveCodexAppServerModelProvider, } from "./provider-config.js";
|
|
10
10
|
import { containsKnownSecret, countAvailableModels, createCodexSpawnEnv, createSmokeRepo, extractAgentText, extractNotificationMethod, isApiKeyAccount, modelListContains, readAccountType, readGitStatus, readThreadId, readTurnStatus, redactWithKnownValues, renderNoWriteSmokePrompt, } from "./smoke-helpers.js";
|
|
11
11
|
import { resolveCodexAppServerSmokeProviderInput } from "./smoke-provider-input.js";
|
|
@@ -68,7 +68,7 @@ export async function runCodexAppServerNoWriteSmoke(options) {
|
|
|
68
68
|
model,
|
|
69
69
|
modelProvider,
|
|
70
70
|
approvalPolicy: "never",
|
|
71
|
-
|
|
71
|
+
sandbox: "read-only",
|
|
72
72
|
ephemeral: true,
|
|
73
73
|
experimentalRawEvents: false,
|
|
74
74
|
persistExtendedHistory: false,
|
|
@@ -88,7 +88,7 @@ export async function runCodexAppServerNoWriteSmoke(options) {
|
|
|
88
88
|
],
|
|
89
89
|
cwd: repoRoot,
|
|
90
90
|
approvalPolicy: "never",
|
|
91
|
-
|
|
91
|
+
sandboxPolicy: createReadOnlySandboxPolicy(),
|
|
92
92
|
model,
|
|
93
93
|
modelProvider,
|
|
94
94
|
outputSchema: CODEX_APP_SERVER_READ_ONLY_SMOKE_OUTPUT_SCHEMA,
|
|
@@ -5,7 +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 {
|
|
8
|
+
import { createWorkspaceWriteSandboxPolicy } from "./sandbox-policy.js";
|
|
9
9
|
import { buildCodexAppServerProviderConfigOverrides, resolveCodexAppServerModelProvider, } from "./provider-config.js";
|
|
10
10
|
import { buildCodexAppServerTurnInput, resolveCodexAppServerContextRuntimeHint, resolveCodexAppServerSkillsListWarmup, resolveCodexAppServerSkillInputs, CodexAppServerSkillSelectionError, } from "./skills.js";
|
|
11
11
|
import { compactText, containsKnownSecret, createCodexSpawnEnv, extractAgentText, extractServerRequestMethod, findJsonObjectCandidates, isApiKeyAccount, readThreadId, readTurnError, readTurnStatus, redactWithKnownValues, } from "./runtime-helpers.js";
|
|
@@ -248,9 +248,7 @@ export async function runCodexAppServerWorkspaceWriteRun(options) {
|
|
|
248
248
|
modelProvider,
|
|
249
249
|
approvalPolicy,
|
|
250
250
|
approvalsReviewer,
|
|
251
|
-
|
|
252
|
-
networkEnabled,
|
|
253
|
-
}),
|
|
251
|
+
sandbox: "workspace-write",
|
|
254
252
|
config: buildWorkspaceWriteThreadConfig(options),
|
|
255
253
|
ephemeral: true,
|
|
256
254
|
experimentalRawEvents: false,
|
|
@@ -272,7 +270,7 @@ export async function runCodexAppServerWorkspaceWriteRun(options) {
|
|
|
272
270
|
cwd: options.workspaceRoot,
|
|
273
271
|
approvalPolicy,
|
|
274
272
|
approvalsReviewer,
|
|
275
|
-
|
|
273
|
+
sandboxPolicy: createWorkspaceWriteSandboxPolicy(options.workspaceRoot, {
|
|
276
274
|
networkEnabled,
|
|
277
275
|
}),
|
|
278
276
|
model,
|
|
@@ -43,21 +43,12 @@ export type CodexAppServerThreadStartParams = {
|
|
|
43
43
|
experimentalRawEvents: false;
|
|
44
44
|
persistExtendedHistory: false;
|
|
45
45
|
};
|
|
46
|
-
export type CodexAppServerReadOnlyAccess = {
|
|
47
|
-
type: "restricted";
|
|
48
|
-
includePlatformDefaults: boolean;
|
|
49
|
-
readableRoots: string[];
|
|
50
|
-
} | {
|
|
51
|
-
type: "fullAccess";
|
|
52
|
-
};
|
|
53
46
|
export type CodexAppServerSandboxPolicy = {
|
|
54
47
|
type: "readOnly";
|
|
55
|
-
access: CodexAppServerReadOnlyAccess;
|
|
56
48
|
networkAccess: boolean;
|
|
57
49
|
} | {
|
|
58
50
|
type: "workspaceWrite";
|
|
59
51
|
writableRoots: string[];
|
|
60
|
-
readOnlyAccess: CodexAppServerReadOnlyAccess;
|
|
61
52
|
networkAccess: boolean;
|
|
62
53
|
excludeTmpdirEnvVar: boolean;
|
|
63
54
|
excludeSlashTmp: boolean;
|
|
@@ -107,11 +107,6 @@ export function createCodexAppServerNoWriteSmokePlan(options) {
|
|
|
107
107
|
approvalPolicy: "never",
|
|
108
108
|
sandboxPolicy: {
|
|
109
109
|
type: "readOnly",
|
|
110
|
-
access: {
|
|
111
|
-
type: "restricted",
|
|
112
|
-
includePlatformDefaults: true,
|
|
113
|
-
readableRoots: [options.workingDirectory],
|
|
114
|
-
},
|
|
115
110
|
networkAccess,
|
|
116
111
|
},
|
|
117
112
|
outputSchema: CODEX_NO_WRITE_SMOKE_OUTPUT_SCHEMA,
|
|
@@ -1,7 +1,49 @@
|
|
|
1
|
+
export type CliCommand = {
|
|
2
|
+
kind: "help";
|
|
3
|
+
topic?: string;
|
|
4
|
+
} | {
|
|
5
|
+
kind: "version";
|
|
6
|
+
} | {
|
|
7
|
+
kind: "launch";
|
|
8
|
+
workspacePath: string;
|
|
9
|
+
yes: boolean;
|
|
10
|
+
foreground: boolean;
|
|
11
|
+
} | {
|
|
12
|
+
kind: "internal-host-run";
|
|
13
|
+
workspacePath: string;
|
|
14
|
+
yes: boolean;
|
|
15
|
+
} | {
|
|
16
|
+
kind: "ps";
|
|
17
|
+
manage: boolean;
|
|
18
|
+
} | {
|
|
19
|
+
kind: "stop";
|
|
20
|
+
workspacePath?: string;
|
|
21
|
+
} | {
|
|
22
|
+
kind: "restart";
|
|
23
|
+
workspacePath?: string;
|
|
24
|
+
yes: boolean;
|
|
25
|
+
} | {
|
|
26
|
+
kind: "invite";
|
|
27
|
+
workspacePath?: string;
|
|
28
|
+
} | {
|
|
29
|
+
kind: "logs";
|
|
30
|
+
workspacePath?: string;
|
|
31
|
+
tail: number;
|
|
32
|
+
} | {
|
|
33
|
+
kind: "provider-setup";
|
|
34
|
+
workspacePath?: string;
|
|
35
|
+
} | {
|
|
36
|
+
kind: "provider-status";
|
|
37
|
+
workspacePath?: string;
|
|
38
|
+
} | {
|
|
39
|
+
kind: "doctor";
|
|
40
|
+
workspacePath?: string;
|
|
41
|
+
};
|
|
1
42
|
export type LaunchCliArgs = {
|
|
2
43
|
command: "launch";
|
|
3
44
|
workspacePath: string;
|
|
4
45
|
yes: boolean;
|
|
5
46
|
};
|
|
6
47
|
export declare function parseLaunchCliArgs(argv: readonly string[], cwd?: string): LaunchCliArgs;
|
|
48
|
+
export declare function parseCliArgs(argv: readonly string[], cwd?: string): CliCommand;
|
|
7
49
|
//# sourceMappingURL=args.d.ts.map
|
|
@@ -1,5 +1,11 @@
|
|
|
1
1
|
import { resolve } from "node:path";
|
|
2
2
|
import { LaunchError } from "./errors.js";
|
|
3
|
+
function isHelpArg(arg) {
|
|
4
|
+
return arg === "--help" || arg === "-h";
|
|
5
|
+
}
|
|
6
|
+
function hasHelpArg(args) {
|
|
7
|
+
return args.some((arg) => isHelpArg(arg));
|
|
8
|
+
}
|
|
3
9
|
export function parseLaunchCliArgs(argv, cwd = process.cwd()) {
|
|
4
10
|
const args = [...argv];
|
|
5
11
|
const command = args.shift();
|
|
@@ -27,4 +33,183 @@ export function parseLaunchCliArgs(argv, cwd = process.cwd()) {
|
|
|
27
33
|
yes,
|
|
28
34
|
};
|
|
29
35
|
}
|
|
36
|
+
function readOptionalWorkspace(args, cwd) {
|
|
37
|
+
let workspacePath;
|
|
38
|
+
for (const arg of args) {
|
|
39
|
+
if (arg.startsWith("--")) {
|
|
40
|
+
throw new LaunchError("git_bootstrap_failed", `Unsupported option: ${arg}`, "Run `tutti --help` to see supported commands.");
|
|
41
|
+
}
|
|
42
|
+
if (workspacePath !== undefined) {
|
|
43
|
+
throw new LaunchError("git_bootstrap_failed", "Only one workspace_path argument is supported", "Run `tutti --help` to see supported commands.");
|
|
44
|
+
}
|
|
45
|
+
workspacePath = resolve(cwd, arg);
|
|
46
|
+
}
|
|
47
|
+
return workspacePath;
|
|
48
|
+
}
|
|
49
|
+
export function parseCliArgs(argv, cwd = process.cwd()) {
|
|
50
|
+
const args = [...argv];
|
|
51
|
+
const command = args.shift();
|
|
52
|
+
if (command === undefined ||
|
|
53
|
+
command === "--help" ||
|
|
54
|
+
command === "-h" ||
|
|
55
|
+
command === "help") {
|
|
56
|
+
return { kind: "help", ...(args[0] === undefined ? {} : { topic: args[0] }) };
|
|
57
|
+
}
|
|
58
|
+
if (command === "--version" || command === "-v" || command === "version") {
|
|
59
|
+
return { kind: "version" };
|
|
60
|
+
}
|
|
61
|
+
if (command === "launch") {
|
|
62
|
+
let workspacePath;
|
|
63
|
+
let yes = false;
|
|
64
|
+
let foreground = false;
|
|
65
|
+
for (const arg of args) {
|
|
66
|
+
if (arg === "--yes") {
|
|
67
|
+
yes = true;
|
|
68
|
+
continue;
|
|
69
|
+
}
|
|
70
|
+
if (arg === "--foreground") {
|
|
71
|
+
foreground = true;
|
|
72
|
+
continue;
|
|
73
|
+
}
|
|
74
|
+
if (isHelpArg(arg)) {
|
|
75
|
+
return { kind: "help", topic: "launch" };
|
|
76
|
+
}
|
|
77
|
+
if (arg.startsWith("--")) {
|
|
78
|
+
throw new LaunchError("git_bootstrap_failed", `Unsupported option: ${arg}`, "Use `tutti launch --help` to see supported launch options.");
|
|
79
|
+
}
|
|
80
|
+
if (workspacePath !== undefined) {
|
|
81
|
+
throw new LaunchError("git_bootstrap_failed", "Only one workspace_path argument is supported", "Run `tutti launch [workspace_path] [--yes] [--foreground]`.");
|
|
82
|
+
}
|
|
83
|
+
workspacePath = resolve(cwd, arg);
|
|
84
|
+
}
|
|
85
|
+
return { kind: "launch", workspacePath: workspacePath ?? resolve(cwd, "."), yes, foreground };
|
|
86
|
+
}
|
|
87
|
+
if (command === "internal") {
|
|
88
|
+
const internalCommand = args.shift();
|
|
89
|
+
if (internalCommand !== "host-run") {
|
|
90
|
+
throw new LaunchError("git_bootstrap_failed", "Unsupported internal command", "Internal commands are managed by Tutti.");
|
|
91
|
+
}
|
|
92
|
+
let workspacePath;
|
|
93
|
+
let yes = false;
|
|
94
|
+
for (let index = 0; index < args.length; index += 1) {
|
|
95
|
+
const arg = args[index];
|
|
96
|
+
if (arg === "--yes") {
|
|
97
|
+
yes = true;
|
|
98
|
+
continue;
|
|
99
|
+
}
|
|
100
|
+
if (arg === "--workspace") {
|
|
101
|
+
const value = args[index + 1];
|
|
102
|
+
if (value === undefined) {
|
|
103
|
+
throw new LaunchError("git_bootstrap_failed", "--workspace requires a path", "Internal host-run commands are managed by Tutti.");
|
|
104
|
+
}
|
|
105
|
+
workspacePath = resolve(cwd, value);
|
|
106
|
+
index += 1;
|
|
107
|
+
continue;
|
|
108
|
+
}
|
|
109
|
+
throw new LaunchError("git_bootstrap_failed", `Unsupported internal option: ${arg ?? ""}`, "Internal commands are managed by Tutti.");
|
|
110
|
+
}
|
|
111
|
+
if (workspacePath === undefined) {
|
|
112
|
+
throw new LaunchError("git_bootstrap_failed", "Internal host-run requires --workspace", "Internal commands are managed by Tutti.");
|
|
113
|
+
}
|
|
114
|
+
return { kind: "internal-host-run", workspacePath, yes };
|
|
115
|
+
}
|
|
116
|
+
if (command === "ps") {
|
|
117
|
+
let manage = false;
|
|
118
|
+
for (const arg of args) {
|
|
119
|
+
if (arg === "--manage") {
|
|
120
|
+
manage = true;
|
|
121
|
+
continue;
|
|
122
|
+
}
|
|
123
|
+
if (isHelpArg(arg)) {
|
|
124
|
+
return { kind: "help", topic: "ps" };
|
|
125
|
+
}
|
|
126
|
+
throw new LaunchError("git_bootstrap_failed", `Unsupported option: ${arg}`, "Use `tutti ps --help` to see supported ps options.");
|
|
127
|
+
}
|
|
128
|
+
return { kind: "ps", manage };
|
|
129
|
+
}
|
|
130
|
+
if (command === "stop") {
|
|
131
|
+
if (hasHelpArg(args)) {
|
|
132
|
+
return { kind: "help", topic: "stop" };
|
|
133
|
+
}
|
|
134
|
+
const workspacePath = readOptionalWorkspace(args, cwd);
|
|
135
|
+
return { kind: "stop", ...(workspacePath === undefined ? {} : { workspacePath }) };
|
|
136
|
+
}
|
|
137
|
+
if (command === "restart") {
|
|
138
|
+
if (hasHelpArg(args)) {
|
|
139
|
+
return { kind: "help", topic: "restart" };
|
|
140
|
+
}
|
|
141
|
+
let yes = false;
|
|
142
|
+
const positional = [];
|
|
143
|
+
for (const arg of args) {
|
|
144
|
+
if (arg === "--yes") {
|
|
145
|
+
yes = true;
|
|
146
|
+
}
|
|
147
|
+
else {
|
|
148
|
+
positional.push(arg);
|
|
149
|
+
}
|
|
150
|
+
}
|
|
151
|
+
const workspacePath = readOptionalWorkspace(positional, cwd);
|
|
152
|
+
return { kind: "restart", yes, ...(workspacePath === undefined ? {} : { workspacePath }) };
|
|
153
|
+
}
|
|
154
|
+
if (command === "invite") {
|
|
155
|
+
if (hasHelpArg(args)) {
|
|
156
|
+
return { kind: "help", topic: "invite" };
|
|
157
|
+
}
|
|
158
|
+
const workspacePath = readOptionalWorkspace(args, cwd);
|
|
159
|
+
return { kind: "invite", ...(workspacePath === undefined ? {} : { workspacePath }) };
|
|
160
|
+
}
|
|
161
|
+
if (command === "logs") {
|
|
162
|
+
if (hasHelpArg(args)) {
|
|
163
|
+
return { kind: "help", topic: "logs" };
|
|
164
|
+
}
|
|
165
|
+
let tail = 120;
|
|
166
|
+
const positional = [];
|
|
167
|
+
for (let index = 0; index < args.length; index += 1) {
|
|
168
|
+
const arg = args[index];
|
|
169
|
+
if (arg === "--tail") {
|
|
170
|
+
const value = args[index + 1];
|
|
171
|
+
const parsed = Number(value);
|
|
172
|
+
if (!Number.isInteger(parsed) || parsed <= 0) {
|
|
173
|
+
throw new LaunchError("git_bootstrap_failed", "--tail requires a positive integer", "Run `tutti logs [workspace_path] --tail 120`.");
|
|
174
|
+
}
|
|
175
|
+
tail = parsed;
|
|
176
|
+
index += 1;
|
|
177
|
+
}
|
|
178
|
+
else {
|
|
179
|
+
positional.push(arg ?? "");
|
|
180
|
+
}
|
|
181
|
+
}
|
|
182
|
+
const workspacePath = readOptionalWorkspace(positional, cwd);
|
|
183
|
+
return { kind: "logs", tail, ...(workspacePath === undefined ? {} : { workspacePath }) };
|
|
184
|
+
}
|
|
185
|
+
if (command === "provider") {
|
|
186
|
+
const providerCommand = args.shift();
|
|
187
|
+
if (providerCommand === undefined || isHelpArg(providerCommand)) {
|
|
188
|
+
return { kind: "help", topic: "provider" };
|
|
189
|
+
}
|
|
190
|
+
if (providerCommand === "setup") {
|
|
191
|
+
if (hasHelpArg(args)) {
|
|
192
|
+
return { kind: "help", topic: "provider" };
|
|
193
|
+
}
|
|
194
|
+
const workspacePath = readOptionalWorkspace(args, cwd);
|
|
195
|
+
return { kind: "provider-setup", ...(workspacePath === undefined ? {} : { workspacePath }) };
|
|
196
|
+
}
|
|
197
|
+
if (providerCommand === "status") {
|
|
198
|
+
if (hasHelpArg(args)) {
|
|
199
|
+
return { kind: "help", topic: "provider" };
|
|
200
|
+
}
|
|
201
|
+
const workspacePath = readOptionalWorkspace(args, cwd);
|
|
202
|
+
return { kind: "provider-status", ...(workspacePath === undefined ? {} : { workspacePath }) };
|
|
203
|
+
}
|
|
204
|
+
return { kind: "help", topic: "provider" };
|
|
205
|
+
}
|
|
206
|
+
if (command === "doctor") {
|
|
207
|
+
if (hasHelpArg(args)) {
|
|
208
|
+
return { kind: "help", topic: "doctor" };
|
|
209
|
+
}
|
|
210
|
+
const workspacePath = readOptionalWorkspace(args, cwd);
|
|
211
|
+
return { kind: "doctor", ...(workspacePath === undefined ? {} : { workspacePath }) };
|
|
212
|
+
}
|
|
213
|
+
throw new LaunchError("git_bootstrap_failed", `Unsupported command: ${command}`, "Run `tutti --help` to see supported commands.");
|
|
214
|
+
}
|
|
30
215
|
//# sourceMappingURL=args.js.map
|