@prover-coder-ai/docker-git 1.0.11 → 1.0.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/.package.json.release.bak +1 -1
- package/CHANGELOG.md +6 -0
- package/README.md +5 -1
- package/dist/src/docker-git/cli/parser-mcp-playwright.js +18 -0
- package/dist/src/docker-git/cli/parser.js +3 -2
- package/dist/src/docker-git/cli/usage.js +2 -0
- package/dist/src/docker-git/program.js +2 -1
- package/package.json +1 -1
- package/src/docker-git/cli/parser-mcp-playwright.ts +25 -0
- package/src/docker-git/cli/parser.ts +7 -3
- package/src/docker-git/cli/usage.ts +2 -0
- package/src/docker-git/program.ts +5 -1
- package/tests/docker-git/parser.test.ts +28 -0
package/CHANGELOG.md
CHANGED
package/README.md
CHANGED
|
@@ -93,6 +93,9 @@ Disable sharing (per-project auth):
|
|
|
93
93
|
Enable during create/clone:
|
|
94
94
|
- Add `--mcp-playwright`
|
|
95
95
|
|
|
96
|
+
Enable for an existing project directory (preserves `.orch/env/project.env` and volumes):
|
|
97
|
+
- `docker-git mcp-playwright [<url>] [--project-dir <path>]`
|
|
98
|
+
|
|
96
99
|
This will:
|
|
97
100
|
- Create a Chromium sidecar container: `dg-<repo>-browser`
|
|
98
101
|
- Configure Codex MCP server `playwright` inside the dev container
|
|
@@ -119,7 +122,8 @@ Common toggles:
|
|
|
119
122
|
MCP errors in `codex` UI:
|
|
120
123
|
- `No such file or directory (os error 2)` for `playwright`:
|
|
121
124
|
- `~/.codex/config.toml` contains `[mcp_servers.playwright]`, but the container was created without `--mcp-playwright`.
|
|
122
|
-
- Fix:
|
|
125
|
+
- Fix (recommended): run `docker-git mcp-playwright [<url>]` to enable it for the existing project.
|
|
126
|
+
- Fix (recreate): recreate with `--force-env --mcp-playwright` (keeps volumes) or `--force --mcp-playwright` (wipes volumes).
|
|
123
127
|
- `handshaking ... initialize response`:
|
|
124
128
|
- The configured MCP command is not a real MCP server (example: `command="echo"`).
|
|
125
129
|
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
import { Either } from "effect";
|
|
2
|
+
import {} from "@effect-template/lib/core/domain";
|
|
3
|
+
import { parseProjectDirWithOptions } from "./parser-shared.js";
|
|
4
|
+
// CHANGE: parse "mcp-playwright" command for existing docker-git projects
|
|
5
|
+
// WHY: allow enabling Playwright MCP in an already created container/project dir
|
|
6
|
+
// QUOTE(ТЗ): "Добавить возможность поднимать MCP Playrgiht в контейнере который уже создан"
|
|
7
|
+
// REF: issue-29
|
|
8
|
+
// SOURCE: n/a
|
|
9
|
+
// FORMAT THEOREM: forall argv: parseMcpPlaywright(argv) = cmd -> deterministic(cmd)
|
|
10
|
+
// PURITY: CORE
|
|
11
|
+
// EFFECT: Effect<McpPlaywrightUpCommand, ParseError, never>
|
|
12
|
+
// INVARIANT: projectDir is never empty
|
|
13
|
+
// COMPLEXITY: O(n) where n = |argv|
|
|
14
|
+
export const parseMcpPlaywright = (args) => Either.map(parseProjectDirWithOptions(args), ({ projectDir, raw }) => ({
|
|
15
|
+
_tag: "McpPlaywrightUp",
|
|
16
|
+
projectDir,
|
|
17
|
+
runUp: raw.up ?? true
|
|
18
|
+
}));
|
|
@@ -4,6 +4,7 @@ import { parseAttach } from "./parser-attach.js";
|
|
|
4
4
|
import { parseAuth } from "./parser-auth.js";
|
|
5
5
|
import { parseClone } from "./parser-clone.js";
|
|
6
6
|
import { buildCreateCommand } from "./parser-create.js";
|
|
7
|
+
import { parseMcpPlaywright } from "./parser-mcp-playwright.js";
|
|
7
8
|
import { parseRawOptions } from "./parser-options.js";
|
|
8
9
|
import { parsePanes } from "./parser-panes.js";
|
|
9
10
|
import { parseScrap } from "./parser-scrap.js";
|
|
@@ -40,6 +41,6 @@ export const parseArgs = (args) => {
|
|
|
40
41
|
command: command ?? ""
|
|
41
42
|
};
|
|
42
43
|
return Match.value(command)
|
|
43
|
-
.pipe(Match.when("create", () => parseCreate(rest)), Match.when("init", () => parseCreate(rest)), Match.when("clone", () => parseClone(rest)), Match.when("attach", () => parseAttach(rest)), Match.when("tmux", () => parseAttach(rest)), Match.when("panes", () => parsePanes(rest)), Match.when("terms", () => parsePanes(rest)), Match.when("terminals", () => parsePanes(rest)), Match.when("sessions", () => parseSessions(rest)), Match.when("scrap", () => parseScrap(rest)), Match.when("help", () => Either.right(helpCommand)), Match.when("ps", () => Either.right(statusCommand)), Match.when("status", () => Either.right(statusCommand)), Match.when("down-all", () => Either.right(downAllCommand)), Match.when("stop-all", () => Either.right(downAllCommand)), Match.when("kill-all", () => Either.right(downAllCommand)), Match.when("menu", () => Either.right(menuCommand)), Match.when("ui", () => Either.right(menuCommand)), Match.when("auth", () => parseAuth(rest))
|
|
44
|
-
.pipe(Match.orElse(() => Either.left(unknownCommandError)));
|
|
44
|
+
.pipe(Match.when("create", () => parseCreate(rest)), Match.when("init", () => parseCreate(rest)), Match.when("clone", () => parseClone(rest)), Match.when("attach", () => parseAttach(rest)), Match.when("tmux", () => parseAttach(rest)), Match.when("panes", () => parsePanes(rest)), Match.when("terms", () => parsePanes(rest)), Match.when("terminals", () => parsePanes(rest)), Match.when("sessions", () => parseSessions(rest)), Match.when("scrap", () => parseScrap(rest)), Match.when("mcp-playwright", () => parseMcpPlaywright(rest)), Match.when("help", () => Either.right(helpCommand)), Match.when("ps", () => Either.right(statusCommand)), Match.when("status", () => Either.right(statusCommand)), Match.when("down-all", () => Either.right(downAllCommand)), Match.when("stop-all", () => Either.right(downAllCommand)), Match.when("kill-all", () => Either.right(downAllCommand)), Match.when("menu", () => Either.right(menuCommand)), Match.when("ui", () => Either.right(menuCommand)), Match.when("auth", () => parseAuth(rest)))
|
|
45
|
+
.pipe(Match.when("state", () => parseState(rest)), Match.orElse(() => Either.left(unknownCommandError)));
|
|
45
46
|
};
|
|
@@ -2,6 +2,7 @@ import { Match } from "effect";
|
|
|
2
2
|
export const usageText = `docker-git menu
|
|
3
3
|
docker-git create --repo-url <url> [options]
|
|
4
4
|
docker-git clone <url> [options]
|
|
5
|
+
docker-git mcp-playwright [<url>] [options]
|
|
5
6
|
docker-git attach [<url>] [options]
|
|
6
7
|
docker-git panes [<url>] [options]
|
|
7
8
|
docker-git scrap <action> [<url>] [options]
|
|
@@ -17,6 +18,7 @@ Commands:
|
|
|
17
18
|
menu Interactive menu (default when no args)
|
|
18
19
|
create, init Generate docker development environment
|
|
19
20
|
clone Create + run container and clone repo
|
|
21
|
+
mcp-playwright Enable Playwright MCP + Chromium sidecar for an existing project dir
|
|
20
22
|
attach, tmux Open tmux workspace for a docker-git project
|
|
21
23
|
panes, terms List tmux panes for a docker-git project
|
|
22
24
|
scrap Export/import project scrap (session snapshot + rebuildable deps)
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { createProject } from "@effect-template/lib/usecases/actions";
|
|
2
2
|
import { authCodexLogin, authCodexLogout, authCodexStatus, authGithubLogin, authGithubLogout, authGithubStatus } from "@effect-template/lib/usecases/auth";
|
|
3
3
|
import { renderError } from "@effect-template/lib/usecases/errors";
|
|
4
|
+
import { mcpPlaywrightUp } from "@effect-template/lib/usecases/mcp-playwright";
|
|
4
5
|
import { downAllDockerGitProjects, listProjectStatus } from "@effect-template/lib/usecases/projects";
|
|
5
6
|
import { exportScrap, importScrap } from "@effect-template/lib/usecases/scrap";
|
|
6
7
|
import { stateCommit, stateInit, statePath, statePull, statePush, stateStatus, stateSync } from "@effect-template/lib/usecases/state-repo";
|
|
@@ -22,7 +23,7 @@ const logWarningAndExit = (error) => pipe(Effect.logWarning(renderError(error)),
|
|
|
22
23
|
const logErrorAndExit = (error) => pipe(Effect.logError(renderError(error)), Effect.tap(() => setExitCode(1)), Effect.asVoid);
|
|
23
24
|
const handleNonBaseCommand = (command) => Match.value(command)
|
|
24
25
|
.pipe(Match.when({ _tag: "StatePath" }, () => statePath), Match.when({ _tag: "StateInit" }, (cmd) => stateInit(cmd)), Match.when({ _tag: "StateStatus" }, () => stateStatus), Match.when({ _tag: "StatePull" }, () => statePull), Match.when({ _tag: "StateCommit" }, (cmd) => stateCommit(cmd.message)), Match.when({ _tag: "StatePush" }, () => statePush), Match.when({ _tag: "StateSync" }, (cmd) => stateSync(cmd.message)), Match.when({ _tag: "AuthGithubLogin" }, (cmd) => authGithubLogin(cmd)), Match.when({ _tag: "AuthGithubStatus" }, (cmd) => authGithubStatus(cmd)), Match.when({ _tag: "AuthGithubLogout" }, (cmd) => authGithubLogout(cmd)), Match.when({ _tag: "AuthCodexLogin" }, (cmd) => authCodexLogin(cmd)), Match.when({ _tag: "AuthCodexStatus" }, (cmd) => authCodexStatus(cmd)), Match.when({ _tag: "AuthCodexLogout" }, (cmd) => authCodexLogout(cmd)), Match.when({ _tag: "Attach" }, (cmd) => attachTmux(cmd)), Match.when({ _tag: "Panes" }, (cmd) => listTmuxPanes(cmd)), Match.when({ _tag: "SessionsList" }, (cmd) => listTerminalSessions(cmd)), Match.when({ _tag: "SessionsKill" }, (cmd) => killTerminalProcess(cmd)), Match.when({ _tag: "SessionsLogs" }, (cmd) => tailTerminalLogs(cmd)), Match.when({ _tag: "ScrapExport" }, (cmd) => exportScrap(cmd)), Match.when({ _tag: "ScrapImport" }, (cmd) => importScrap(cmd)))
|
|
25
|
-
.pipe(Match.exhaustive);
|
|
26
|
+
.pipe(Match.when({ _tag: "McpPlaywrightUp" }, (cmd) => mcpPlaywrightUp(cmd)), Match.exhaustive);
|
|
26
27
|
// CHANGE: compose CLI program with typed errors and shell effects
|
|
27
28
|
// WHY: keep a thin entry layer over pure parsing and template generation
|
|
28
29
|
// QUOTE(ТЗ): "CLI команду... создавать докер образы"
|
package/package.json
CHANGED
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
import { Either } from "effect"
|
|
2
|
+
|
|
3
|
+
import { type McpPlaywrightUpCommand, type ParseError } from "@effect-template/lib/core/domain"
|
|
4
|
+
|
|
5
|
+
import { parseProjectDirWithOptions } from "./parser-shared.js"
|
|
6
|
+
|
|
7
|
+
// CHANGE: parse "mcp-playwright" command for existing docker-git projects
|
|
8
|
+
// WHY: allow enabling Playwright MCP in an already created container/project dir
|
|
9
|
+
// QUOTE(ТЗ): "Добавить возможность поднимать MCP Playrgiht в контейнере который уже создан"
|
|
10
|
+
// REF: issue-29
|
|
11
|
+
// SOURCE: n/a
|
|
12
|
+
// FORMAT THEOREM: forall argv: parseMcpPlaywright(argv) = cmd -> deterministic(cmd)
|
|
13
|
+
// PURITY: CORE
|
|
14
|
+
// EFFECT: Effect<McpPlaywrightUpCommand, ParseError, never>
|
|
15
|
+
// INVARIANT: projectDir is never empty
|
|
16
|
+
// COMPLEXITY: O(n) where n = |argv|
|
|
17
|
+
export const parseMcpPlaywright = (
|
|
18
|
+
args: ReadonlyArray<string>
|
|
19
|
+
): Either.Either<McpPlaywrightUpCommand, ParseError> =>
|
|
20
|
+
Either.map(parseProjectDirWithOptions(args), ({ projectDir, raw }) => ({
|
|
21
|
+
_tag: "McpPlaywrightUp",
|
|
22
|
+
projectDir,
|
|
23
|
+
runUp: raw.up ?? true
|
|
24
|
+
}))
|
|
25
|
+
|
|
@@ -6,6 +6,7 @@ import { parseAttach } from "./parser-attach.js"
|
|
|
6
6
|
import { parseAuth } from "./parser-auth.js"
|
|
7
7
|
import { parseClone } from "./parser-clone.js"
|
|
8
8
|
import { buildCreateCommand } from "./parser-create.js"
|
|
9
|
+
import { parseMcpPlaywright } from "./parser-mcp-playwright.js"
|
|
9
10
|
import { parseRawOptions } from "./parser-options.js"
|
|
10
11
|
import { parsePanes } from "./parser-panes.js"
|
|
11
12
|
import { parseScrap } from "./parser-scrap.js"
|
|
@@ -61,6 +62,7 @@ export const parseArgs = (args: ReadonlyArray<string>): Either.Either<Command, P
|
|
|
61
62
|
Match.when("terminals", () => parsePanes(rest)),
|
|
62
63
|
Match.when("sessions", () => parseSessions(rest)),
|
|
63
64
|
Match.when("scrap", () => parseScrap(rest)),
|
|
65
|
+
Match.when("mcp-playwright", () => parseMcpPlaywright(rest)),
|
|
64
66
|
Match.when("help", () => Either.right(helpCommand)),
|
|
65
67
|
Match.when("ps", () => Either.right(statusCommand)),
|
|
66
68
|
Match.when("status", () => Either.right(statusCommand)),
|
|
@@ -69,8 +71,10 @@ export const parseArgs = (args: ReadonlyArray<string>): Either.Either<Command, P
|
|
|
69
71
|
Match.when("kill-all", () => Either.right(downAllCommand)),
|
|
70
72
|
Match.when("menu", () => Either.right(menuCommand)),
|
|
71
73
|
Match.when("ui", () => Either.right(menuCommand)),
|
|
72
|
-
Match.when("auth", () => parseAuth(rest))
|
|
73
|
-
|
|
74
|
+
Match.when("auth", () => parseAuth(rest))
|
|
75
|
+
)
|
|
76
|
+
.pipe(
|
|
77
|
+
Match.when("state", () => parseState(rest)),
|
|
78
|
+
Match.orElse(() => Either.left(unknownCommandError))
|
|
74
79
|
)
|
|
75
|
-
.pipe(Match.orElse(() => Either.left(unknownCommandError)))
|
|
76
80
|
}
|
|
@@ -5,6 +5,7 @@ import type { ParseError } from "@effect-template/lib/core/domain"
|
|
|
5
5
|
export const usageText = `docker-git menu
|
|
6
6
|
docker-git create --repo-url <url> [options]
|
|
7
7
|
docker-git clone <url> [options]
|
|
8
|
+
docker-git mcp-playwright [<url>] [options]
|
|
8
9
|
docker-git attach [<url>] [options]
|
|
9
10
|
docker-git panes [<url>] [options]
|
|
10
11
|
docker-git scrap <action> [<url>] [options]
|
|
@@ -20,6 +21,7 @@ Commands:
|
|
|
20
21
|
menu Interactive menu (default when no args)
|
|
21
22
|
create, init Generate docker development environment
|
|
22
23
|
clone Create + run container and clone repo
|
|
24
|
+
mcp-playwright Enable Playwright MCP + Chromium sidecar for an existing project dir
|
|
23
25
|
attach, tmux Open tmux workspace for a docker-git project
|
|
24
26
|
panes, terms List tmux panes for a docker-git project
|
|
25
27
|
scrap Export/import project scrap (session snapshot + rebuildable deps)
|
|
@@ -10,6 +10,7 @@ import {
|
|
|
10
10
|
} from "@effect-template/lib/usecases/auth"
|
|
11
11
|
import type { AppError } from "@effect-template/lib/usecases/errors"
|
|
12
12
|
import { renderError } from "@effect-template/lib/usecases/errors"
|
|
13
|
+
import { mcpPlaywrightUp } from "@effect-template/lib/usecases/mcp-playwright"
|
|
13
14
|
import { downAllDockerGitProjects, listProjectStatus } from "@effect-template/lib/usecases/projects"
|
|
14
15
|
import { exportScrap, importScrap } from "@effect-template/lib/usecases/scrap"
|
|
15
16
|
import {
|
|
@@ -92,7 +93,10 @@ const handleNonBaseCommand = (command: NonBaseCommand) =>
|
|
|
92
93
|
Match.when({ _tag: "ScrapExport" }, (cmd) => exportScrap(cmd)),
|
|
93
94
|
Match.when({ _tag: "ScrapImport" }, (cmd) => importScrap(cmd))
|
|
94
95
|
)
|
|
95
|
-
.pipe(
|
|
96
|
+
.pipe(
|
|
97
|
+
Match.when({ _tag: "McpPlaywrightUp" }, (cmd) => mcpPlaywrightUp(cmd)),
|
|
98
|
+
Match.exhaustive
|
|
99
|
+
)
|
|
96
100
|
|
|
97
101
|
// CHANGE: compose CLI program with typed errors and shell effects
|
|
98
102
|
// WHY: keep a thin entry layer over pure parsing and template generation
|
|
@@ -152,6 +152,34 @@ describe("parseArgs", () => {
|
|
|
152
152
|
expect(command.projectDir).toBe(".docker-git/org/repo/issue-7")
|
|
153
153
|
}))
|
|
154
154
|
|
|
155
|
+
it.effect("parses mcp-playwright command in current directory", () =>
|
|
156
|
+
Effect.sync(() => {
|
|
157
|
+
const command = parseOrThrow(["mcp-playwright"])
|
|
158
|
+
if (command._tag !== "McpPlaywrightUp") {
|
|
159
|
+
throw new Error("expected McpPlaywrightUp command")
|
|
160
|
+
}
|
|
161
|
+
expect(command.projectDir).toBe(".")
|
|
162
|
+
expect(command.runUp).toBe(true)
|
|
163
|
+
}))
|
|
164
|
+
|
|
165
|
+
it.effect("parses mcp-playwright command with --no-up", () =>
|
|
166
|
+
Effect.sync(() => {
|
|
167
|
+
const command = parseOrThrow(["mcp-playwright", "--no-up"])
|
|
168
|
+
if (command._tag !== "McpPlaywrightUp") {
|
|
169
|
+
throw new Error("expected McpPlaywrightUp command")
|
|
170
|
+
}
|
|
171
|
+
expect(command.runUp).toBe(false)
|
|
172
|
+
}))
|
|
173
|
+
|
|
174
|
+
it.effect("parses mcp-playwright with positional repo url into project dir", () =>
|
|
175
|
+
Effect.sync(() => {
|
|
176
|
+
const command = parseOrThrow(["mcp-playwright", "https://github.com/org/repo.git"])
|
|
177
|
+
if (command._tag !== "McpPlaywrightUp") {
|
|
178
|
+
throw new Error("expected McpPlaywrightUp command")
|
|
179
|
+
}
|
|
180
|
+
expect(command.projectDir).toBe(".docker-git/org/repo")
|
|
181
|
+
}))
|
|
182
|
+
|
|
155
183
|
it.effect("parses down-all command", () =>
|
|
156
184
|
Effect.sync(() => {
|
|
157
185
|
const command = parseOrThrow(["down-all"])
|