bcdocker 1.0.0 → 1.0.2
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/LICENSE +1 -1
- package/README.md +75 -104
- package/dist/cli-main.d.ts +2 -0
- package/dist/cli-main.js +12 -0
- package/dist/cli.d.ts +4 -1
- package/dist/cli.js +198 -189
- package/dist/executor.d.ts +6 -0
- package/dist/executor.js +20 -5
- package/dist/server-handlers.d.ts +145 -0
- package/dist/server-handlers.js +147 -0
- package/dist/server.d.ts +3 -1
- package/dist/server.js +29 -148
- package/package.json +11 -6
- package/ps/BCDocker.psm1 +1 -0
- package/ps/Container.ps1 +48 -11
- package/ps/Helpers.ps1 +20 -0
package/dist/cli.js
CHANGED
|
@@ -1,18 +1,20 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
import { Command } from "commander";
|
|
3
|
-
import { runPowerShell, runRawPowerShell } from "./executor.js";
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
3
|
+
import { runPowerShell, runRawPowerShell, psEscape } from "./executor.js";
|
|
4
|
+
/** Builds the CLI (exported for tests). */
|
|
5
|
+
export function createCliProgram() {
|
|
6
|
+
const program = new Command();
|
|
7
|
+
program
|
|
8
|
+
.name("bcd")
|
|
9
|
+
.description("CLI for Business Central Docker container management")
|
|
10
|
+
.version("1.0.2");
|
|
11
|
+
// ── Containers ───────────────────────────────────────────
|
|
12
|
+
program
|
|
13
|
+
.command("list")
|
|
14
|
+
.alias("ls")
|
|
15
|
+
.description("List all BC containers with status")
|
|
16
|
+
.action(async () => {
|
|
17
|
+
const { stdout } = await runRawPowerShell(`
|
|
16
18
|
Import-Module BcContainerHelper -DisableNameChecking -ErrorAction Stop
|
|
17
19
|
$containers = Get-BcContainers
|
|
18
20
|
if ($containers.Count -eq 0) { Write-Output "No BC containers found." }
|
|
@@ -23,202 +25,209 @@ program
|
|
|
23
25
|
}
|
|
24
26
|
}
|
|
25
27
|
`);
|
|
26
|
-
|
|
27
|
-
});
|
|
28
|
-
program
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
28
|
+
console.log(stdout);
|
|
29
|
+
});
|
|
30
|
+
program
|
|
31
|
+
.command("info <container>")
|
|
32
|
+
.description("Show container details: version, status, endpoints")
|
|
33
|
+
.action(async (container) => {
|
|
34
|
+
const c = psEscape(container);
|
|
35
|
+
const { stdout } = await runPowerShell(`
|
|
33
36
|
Import-Module BcContainerHelper -DisableNameChecking -ErrorAction Stop
|
|
34
|
-
$v = Get-BcContainerNavVersion -
|
|
35
|
-
$u = Get-
|
|
36
|
-
$s = docker inspect '${
|
|
37
|
-
Write-Output "Container : ${
|
|
37
|
+
$v = Get-BcContainerNavVersion -containerOrImageName '${c}'
|
|
38
|
+
$u = Get-BCDockerWebClientUrl -ContainerName '${c}' -UseHttps:$false
|
|
39
|
+
$s = docker inspect '${c}' --format '{{.State.Status}}' 2>$null
|
|
40
|
+
Write-Output "Container : ${c}"
|
|
38
41
|
Write-Output "Status : $s"
|
|
39
42
|
Write-Output "BC Version : $v"
|
|
40
43
|
Write-Output "Web Client : $u"
|
|
41
|
-
Write-Output "OData/API : http://${
|
|
42
|
-
Write-Output "Dev Service: http://${
|
|
44
|
+
Write-Output "OData/API : http://${c}:7048/BC/api"
|
|
45
|
+
Write-Output "Dev Service: http://${c}:7049/BC"
|
|
43
46
|
`);
|
|
44
|
-
|
|
45
|
-
});
|
|
46
|
-
program
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
47
|
+
console.log(stdout);
|
|
48
|
+
});
|
|
49
|
+
program
|
|
50
|
+
.command("create")
|
|
51
|
+
.description("Create a new BC container")
|
|
52
|
+
.option("-n, --name <name>", "Container name", "bcsandbox")
|
|
53
|
+
.option("-v, --version <version>", "BC version (sandbox, onprem, 26.0)", "sandbox")
|
|
54
|
+
.option("-c, --country <code>", "Country code (us, w1, gb, nl)", "us")
|
|
55
|
+
.option("-u, --user <username>", "Admin username", "admin")
|
|
56
|
+
.option("-p, --password <password>", "Admin password", "P@ssw0rd!")
|
|
57
|
+
.option("-m, --memory <limit>", "Memory limit", "8G")
|
|
58
|
+
.option("-i, --isolation <mode>", "Isolation: hyperv or process", "hyperv")
|
|
59
|
+
.option("-t, --toolkit <mode>", "Test toolkit: none, libraries, full", "libraries")
|
|
60
|
+
.option("--bypass-cdn", "Skip Azure CDN, use blob storage")
|
|
61
|
+
.option("-l, --license <file>", "Path or URL to .bclicense / .flf license file")
|
|
62
|
+
.action(async (opts) => {
|
|
63
|
+
const includeToolkit = opts.toolkit !== "none" ? "$true" : "$false";
|
|
64
|
+
const libOnly = opts.toolkit === "libraries" ? "$true" : "$false";
|
|
65
|
+
const cdn = opts.bypassCdn ? "-BypassCDN" : "";
|
|
66
|
+
const lic = opts.license ? `-LicenseFile '${psEscape(opts.license)}'` : "";
|
|
67
|
+
console.log(`Creating container '${opts.name}'...`);
|
|
68
|
+
console.log(` Version: ${opts.version}, Country: ${opts.country}, Toolkit: ${opts.toolkit}`);
|
|
69
|
+
console.log(` This may take 5-30 minutes.\n`);
|
|
70
|
+
const { stdout, stderr } = await runPowerShell(`
|
|
68
71
|
New-BCDContainer \`
|
|
69
|
-
-ContainerName '${opts.name}' \`
|
|
70
|
-
-Version '${opts.version}' \`
|
|
71
|
-
-Country '${opts.country}' \`
|
|
72
|
-
-UserName '${opts.user}' \`
|
|
73
|
-
-Password '${opts.password}' \`
|
|
74
|
-
-MemoryLimit '${opts.memory}' \`
|
|
75
|
-
-Isolation '${opts.isolation}' \`
|
|
72
|
+
-ContainerName '${psEscape(opts.name)}' \`
|
|
73
|
+
-Version '${psEscape(opts.version)}' \`
|
|
74
|
+
-Country '${psEscape(opts.country)}' \`
|
|
75
|
+
-UserName '${psEscape(opts.user)}' \`
|
|
76
|
+
-Password '${psEscape(opts.password)}' \`
|
|
77
|
+
-MemoryLimit '${psEscape(opts.memory)}' \`
|
|
78
|
+
-Isolation '${psEscape(opts.isolation)}' \`
|
|
76
79
|
-IncludeTestToolkit ${includeToolkit} \`
|
|
77
80
|
-TestLibrariesOnly ${libOnly} \`
|
|
78
81
|
${cdn} ${lic}
|
|
79
82
|
`, 1_800_000);
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
});
|
|
85
|
-
program
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
});
|
|
93
|
-
program
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
});
|
|
100
|
-
program
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
});
|
|
107
|
-
program
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
});
|
|
114
|
-
program
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
});
|
|
121
|
-
// ── Apps ─────────────────────────────────────────────────
|
|
122
|
-
program
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
83
|
+
if (stdout)
|
|
84
|
+
console.log(stdout);
|
|
85
|
+
if (stderr)
|
|
86
|
+
console.error(stderr);
|
|
87
|
+
});
|
|
88
|
+
program
|
|
89
|
+
.command("remove <container>")
|
|
90
|
+
.alias("rm")
|
|
91
|
+
.description("Remove a BC container")
|
|
92
|
+
.action(async (container) => {
|
|
93
|
+
const { stdout } = await runPowerShell(`Remove-BCDContainer -ContainerName '${psEscape(container)}'`);
|
|
94
|
+
console.log(stdout || `Container '${container}' removed.`);
|
|
95
|
+
});
|
|
96
|
+
program
|
|
97
|
+
.command("start <container>")
|
|
98
|
+
.description("Start a stopped container")
|
|
99
|
+
.action(async (container) => {
|
|
100
|
+
const { stdout } = await runPowerShell(`Start-BCDContainer -ContainerName '${psEscape(container)}'`);
|
|
101
|
+
console.log(stdout || `Container '${container}' started.`);
|
|
102
|
+
});
|
|
103
|
+
program
|
|
104
|
+
.command("stop <container>")
|
|
105
|
+
.description("Stop a running container")
|
|
106
|
+
.action(async (container) => {
|
|
107
|
+
const { stdout } = await runPowerShell(`Stop-BCDContainer -ContainerName '${psEscape(container)}'`);
|
|
108
|
+
console.log(stdout || `Container '${container}' stopped.`);
|
|
109
|
+
});
|
|
110
|
+
program
|
|
111
|
+
.command("restart <container>")
|
|
112
|
+
.description("Restart a container")
|
|
113
|
+
.action(async (container) => {
|
|
114
|
+
const { stdout } = await runPowerShell(`Restart-BCDContainer -ContainerName '${psEscape(container)}'`);
|
|
115
|
+
console.log(stdout || `Container '${container}' restarted.`);
|
|
116
|
+
});
|
|
117
|
+
program
|
|
118
|
+
.command("open <container>")
|
|
119
|
+
.description("Open the BC Web Client in your browser")
|
|
120
|
+
.action(async (container) => {
|
|
121
|
+
const { stdout } = await runPowerShell(`Open-BCDWebClient -ContainerName '${psEscape(container)}'`);
|
|
122
|
+
console.log(stdout || "Opening web client...");
|
|
123
|
+
});
|
|
124
|
+
// ── Apps ─────────────────────────────────────────────────
|
|
125
|
+
program
|
|
126
|
+
.command("apps <container>")
|
|
127
|
+
.description("List apps in a container")
|
|
128
|
+
.option("--publisher <name>", "Filter by publisher")
|
|
129
|
+
.action(async (container, opts) => {
|
|
130
|
+
const c = psEscape(container);
|
|
131
|
+
const filter = opts.publisher
|
|
132
|
+
? `| Where-Object { $_.Publisher -eq '${psEscape(opts.publisher)}' }`
|
|
133
|
+
: "";
|
|
134
|
+
const { stdout } = await runRawPowerShell(`
|
|
131
135
|
Import-Module BcContainerHelper -DisableNameChecking -ErrorAction Stop
|
|
132
|
-
Get-BcContainerAppInfo -containerName '${
|
|
136
|
+
Get-BcContainerAppInfo -containerName '${c}' -tenant default -tenantSpecificProperties ${filter} |
|
|
133
137
|
Select-Object Name, Publisher, Version, IsInstalled, Scope |
|
|
134
138
|
Sort-Object Publisher, Name |
|
|
135
139
|
Format-Table -AutoSize | Out-String -Width 200
|
|
136
140
|
`);
|
|
137
|
-
|
|
138
|
-
});
|
|
139
|
-
program
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
$cred = Get-BCCredential -UserName '${opts.user}' -Password '${opts.password}'
|
|
147
|
-
Install-BCDApp -ContainerName '${container}' -AppFile '${appFile}' -Credential $cred
|
|
141
|
+
console.log(stdout || "No apps found.");
|
|
142
|
+
});
|
|
143
|
+
program
|
|
144
|
+
.command("install <container> <appFile>")
|
|
145
|
+
.description("Install a .app file into a container")
|
|
146
|
+
.option("-u, --user <username>", "Admin username", "admin")
|
|
147
|
+
.option("-p, --password <password>", "Admin password", "P@ssw0rd!")
|
|
148
|
+
.action(async (container, appFile, opts) => {
|
|
149
|
+
const { stdout } = await runPowerShell(`
|
|
150
|
+
$cred = Get-BCCredential -UserName '${psEscape(opts.user)}' -Password '${psEscape(opts.password)}'
|
|
151
|
+
Install-BCDApp -ContainerName '${psEscape(container)}' -AppFile '${psEscape(appFile)}' -Credential $cred
|
|
148
152
|
`);
|
|
149
|
-
|
|
150
|
-
});
|
|
151
|
-
program
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
153
|
+
console.log(stdout || "App installed.");
|
|
154
|
+
});
|
|
155
|
+
program
|
|
156
|
+
.command("uninstall <container> <appName> <publisher>")
|
|
157
|
+
.description("Uninstall an app by name and publisher")
|
|
158
|
+
.action(async (container, appName, publisher) => {
|
|
159
|
+
const c = psEscape(container);
|
|
160
|
+
const an = psEscape(appName);
|
|
161
|
+
const p = psEscape(publisher);
|
|
162
|
+
const { stdout } = await runRawPowerShell(`
|
|
156
163
|
Import-Module BcContainerHelper -DisableNameChecking -ErrorAction Stop
|
|
157
|
-
$sorted = Get-BcContainerAppInfo -containerName '${
|
|
158
|
-
$target = $sorted | Where-Object { $_.Name -eq '${
|
|
159
|
-
if (-not $target) { Write-Output "App '${
|
|
164
|
+
$sorted = Get-BcContainerAppInfo -containerName '${c}' -tenant default -tenantSpecificProperties -sort DependenciesLast
|
|
165
|
+
$target = $sorted | Where-Object { $_.Name -eq '${an}' -and $_.Publisher -eq '${p}' -and $_.IsInstalled }
|
|
166
|
+
if (-not $target) { Write-Output "App '${an}' by '${p}' not found or not installed."; exit }
|
|
160
167
|
foreach ($app in $target) {
|
|
161
|
-
UnInstall-BcContainerApp -name $app.Name -containerName '${
|
|
168
|
+
UnInstall-BcContainerApp -name $app.Name -containerName '${c}' -publisher $app.Publisher -version $app.Version -force
|
|
162
169
|
Write-Output "Uninstalled: $($app.Name) v$($app.Version)"
|
|
163
170
|
}
|
|
164
171
|
`);
|
|
165
|
-
|
|
166
|
-
});
|
|
167
|
-
program
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
$cred = Get-BCCredential -UserName '${opts.user}' -Password '${opts.password}'
|
|
175
|
-
Publish-BCDProject -ContainerName '${container}' -ProjectFolder '${folder}' -Credential $cred
|
|
172
|
+
console.log(stdout);
|
|
173
|
+
});
|
|
174
|
+
program
|
|
175
|
+
.command("publish <container> <projectFolder>")
|
|
176
|
+
.description("Compile and publish an AL project into a container")
|
|
177
|
+
.option("-u, --user <username>", "Admin username", "admin")
|
|
178
|
+
.option("-p, --password <password>", "Admin password", "P@ssw0rd!")
|
|
179
|
+
.action(async (container, folder, opts) => {
|
|
180
|
+
const { stdout } = await runPowerShell(`
|
|
181
|
+
$cred = Get-BCCredential -UserName '${psEscape(opts.user)}' -Password '${psEscape(opts.password)}'
|
|
182
|
+
Publish-BCDProject -ContainerName '${psEscape(container)}' -ProjectFolder '${psEscape(folder)}' -Credential $cred
|
|
176
183
|
`, 300_000);
|
|
177
|
-
|
|
178
|
-
});
|
|
179
|
-
// ── Tests & License ──────────────────────────────────────
|
|
180
|
-
program
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
});
|
|
200
|
-
program
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
$cred = Get-BCCredential -UserName '${opts.user}' -Password '${opts.password}'
|
|
210
|
-
Import-BCDTestToolkit -ContainerName '${container}' -Credential $cred -LibrariesOnly ${libOnly}
|
|
184
|
+
console.log(stdout || "Project compiled and published.");
|
|
185
|
+
});
|
|
186
|
+
// ── Tests & License ──────────────────────────────────────
|
|
187
|
+
program
|
|
188
|
+
.command("test [container]")
|
|
189
|
+
.description("Run AL tests in a container")
|
|
190
|
+
.option("-c, --codeunit <id>", "Test codeunit ID")
|
|
191
|
+
.option("-f, --function <name>", "Test function name")
|
|
192
|
+
.option("-a, --app <folder>", "AL test project folder to compile/publish/run")
|
|
193
|
+
.option("-u, --user <username>", "Admin username", "admin")
|
|
194
|
+
.option("-p, --password <password>", "Admin password", "P@ssw0rd!")
|
|
195
|
+
.action(async (container = "bcsandbox", opts) => {
|
|
196
|
+
const params = [`-ContainerName '${psEscape(container)}'`];
|
|
197
|
+
params.push(`-Credential (Get-BCCredential -UserName '${psEscape(opts.user)}' -Password '${psEscape(opts.password)}')`);
|
|
198
|
+
if (opts.codeunit)
|
|
199
|
+
params.push(`-TestCodeunitId ${opts.codeunit}`);
|
|
200
|
+
if (opts.function)
|
|
201
|
+
params.push(`-TestFunctionName '${psEscape(opts.function)}'`);
|
|
202
|
+
if (opts.app)
|
|
203
|
+
params.push(`-AppProjectFolder '${psEscape(opts.app)}'`);
|
|
204
|
+
const { stdout } = await runPowerShell(`Invoke-BCDTests ${params.join(" ")}`, 600_000);
|
|
205
|
+
console.log(stdout || "Test run complete.");
|
|
206
|
+
});
|
|
207
|
+
program
|
|
208
|
+
.command("toolkit <container>")
|
|
209
|
+
.description("Import BC Test Toolkit into a container")
|
|
210
|
+
.option("--full", "Import full test framework (default: libraries only)")
|
|
211
|
+
.option("-u, --user <username>", "Admin username", "admin")
|
|
212
|
+
.option("-p, --password <password>", "Admin password", "P@ssw0rd!")
|
|
213
|
+
.action(async (container, opts) => {
|
|
214
|
+
const libOnly = opts.full ? "$false" : "$true";
|
|
215
|
+
const { stdout } = await runPowerShell(`
|
|
216
|
+
$cred = Get-BCCredential -UserName '${psEscape(opts.user)}' -Password '${psEscape(opts.password)}'
|
|
217
|
+
Import-BCDTestToolkit -ContainerName '${psEscape(container)}' -Credential $cred -LibrariesOnly ${libOnly}
|
|
211
218
|
`, 300_000);
|
|
212
|
-
|
|
213
|
-
});
|
|
214
|
-
program
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
});
|
|
221
|
-
program
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
219
|
+
console.log(stdout || "Test toolkit imported.");
|
|
220
|
+
});
|
|
221
|
+
program
|
|
222
|
+
.command("license <container> <file>")
|
|
223
|
+
.description("Import a license file into a container")
|
|
224
|
+
.action(async (container, file) => {
|
|
225
|
+
const { stdout } = await runPowerShell(`Import-BCDLicense -ContainerName '${psEscape(container)}' -LicenseFile '${psEscape(file)}'`);
|
|
226
|
+
console.log(stdout || "License imported.");
|
|
227
|
+
});
|
|
228
|
+
return program;
|
|
229
|
+
}
|
|
230
|
+
export async function runCli(argv) {
|
|
231
|
+
const program = createCliProgram();
|
|
232
|
+
await program.parseAsync(argv);
|
|
233
|
+
}
|
package/dist/executor.d.ts
CHANGED
|
@@ -1,3 +1,9 @@
|
|
|
1
|
+
/** Resolves path to BCDocker.psm1 (exported for tests). */
|
|
2
|
+
export declare function resolveModulePath(): string;
|
|
3
|
+
/** Clears cached module path (tests only). */
|
|
4
|
+
export declare function __resetModulePathCacheForTests(): void;
|
|
5
|
+
/** Escapes single quotes for safe interpolation into PowerShell single-quoted strings. */
|
|
6
|
+
export declare function psEscape(value: string): string;
|
|
1
7
|
export interface ExecResult {
|
|
2
8
|
stdout: string;
|
|
3
9
|
stderr: string;
|
package/dist/executor.js
CHANGED
|
@@ -3,7 +3,8 @@ import { resolve, dirname } from "node:path";
|
|
|
3
3
|
import { fileURLToPath } from "node:url";
|
|
4
4
|
import { existsSync } from "node:fs";
|
|
5
5
|
const __dirname = dirname(fileURLToPath(import.meta.url));
|
|
6
|
-
|
|
6
|
+
/** Resolves path to BCDocker.psm1 (exported for tests). */
|
|
7
|
+
export function resolveModulePath() {
|
|
7
8
|
// 1. Environment variable override
|
|
8
9
|
if (process.env.BCD_MODULE_PATH)
|
|
9
10
|
return process.env.BCD_MODULE_PATH;
|
|
@@ -11,20 +12,34 @@ function resolveModulePath() {
|
|
|
11
12
|
const bundled = resolve(__dirname, "../ps/BCDocker.psm1");
|
|
12
13
|
if (existsSync(bundled))
|
|
13
14
|
return bundled;
|
|
14
|
-
// 3. Development layout (dist/../../
|
|
15
|
-
const dev = resolve(__dirname, "../../
|
|
15
|
+
// 3. Development layout (dist/../../BCDocker/BCDocker.psm1)
|
|
16
|
+
const dev = resolve(__dirname, "../../BCDocker/BCDocker.psm1");
|
|
16
17
|
if (existsSync(dev))
|
|
17
18
|
return dev;
|
|
18
19
|
throw new Error("BCDocker.psm1 not found. Set BCD_MODULE_PATH or ensure the ps/ folder exists.");
|
|
19
20
|
}
|
|
20
|
-
|
|
21
|
+
let cachedModulePath = null;
|
|
22
|
+
function getModulePath() {
|
|
23
|
+
if (!cachedModulePath) {
|
|
24
|
+
cachedModulePath = resolveModulePath();
|
|
25
|
+
}
|
|
26
|
+
return cachedModulePath;
|
|
27
|
+
}
|
|
28
|
+
/** Clears cached module path (tests only). */
|
|
29
|
+
export function __resetModulePathCacheForTests() {
|
|
30
|
+
cachedModulePath = null;
|
|
31
|
+
}
|
|
32
|
+
/** Escapes single quotes for safe interpolation into PowerShell single-quoted strings. */
|
|
33
|
+
export function psEscape(value) {
|
|
34
|
+
return value.replace(/'/g, "''");
|
|
35
|
+
}
|
|
21
36
|
// BC management cmdlets require Windows PowerShell 5.1, not pwsh 7.x
|
|
22
37
|
const PS_EXE = "powershell.exe";
|
|
23
38
|
export function runPowerShell(script, timeoutMs = 600_000) {
|
|
24
39
|
const wrappedScript = `
|
|
25
40
|
$ErrorActionPreference = 'Stop'
|
|
26
41
|
[Net.ServicePointManager]::SecurityProtocol = [Net.SecurityProtocolType]::Tls12
|
|
27
|
-
Import-Module '${
|
|
42
|
+
Import-Module '${getModulePath().replace(/\\/g, "\\\\")}' -DisableNameChecking -Force
|
|
28
43
|
${script}
|
|
29
44
|
`;
|
|
30
45
|
return new Promise((resolve, reject) => {
|
|
@@ -0,0 +1,145 @@
|
|
|
1
|
+
export declare function handleListContainers(): Promise<{
|
|
2
|
+
content: [{
|
|
3
|
+
type: "text";
|
|
4
|
+
text: string;
|
|
5
|
+
}];
|
|
6
|
+
}>;
|
|
7
|
+
export declare function handleContainerInfo({ containerName }: {
|
|
8
|
+
containerName: string;
|
|
9
|
+
}): Promise<{
|
|
10
|
+
content: [{
|
|
11
|
+
type: "text";
|
|
12
|
+
text: string;
|
|
13
|
+
}];
|
|
14
|
+
}>;
|
|
15
|
+
export declare function handleCreateContainer({ containerName, version, country, userName, password, memoryLimit, isolation, testToolkit, bypassCDN, licenseFile, }: {
|
|
16
|
+
containerName: string;
|
|
17
|
+
version: string;
|
|
18
|
+
country: string;
|
|
19
|
+
userName: string;
|
|
20
|
+
password: string;
|
|
21
|
+
memoryLimit: string;
|
|
22
|
+
isolation: "hyperv" | "process";
|
|
23
|
+
testToolkit: "none" | "libraries" | "full";
|
|
24
|
+
bypassCDN: boolean;
|
|
25
|
+
licenseFile?: string;
|
|
26
|
+
}): Promise<{
|
|
27
|
+
content: [{
|
|
28
|
+
type: "text";
|
|
29
|
+
text: string;
|
|
30
|
+
}];
|
|
31
|
+
}>;
|
|
32
|
+
export declare function handleRemoveContainer({ containerName }: {
|
|
33
|
+
containerName: string;
|
|
34
|
+
}): Promise<{
|
|
35
|
+
content: [{
|
|
36
|
+
type: "text";
|
|
37
|
+
text: string;
|
|
38
|
+
}];
|
|
39
|
+
}>;
|
|
40
|
+
export declare function handleStartContainer({ containerName }: {
|
|
41
|
+
containerName: string;
|
|
42
|
+
}): Promise<{
|
|
43
|
+
content: [{
|
|
44
|
+
type: "text";
|
|
45
|
+
text: string;
|
|
46
|
+
}];
|
|
47
|
+
}>;
|
|
48
|
+
export declare function handleStopContainer({ containerName }: {
|
|
49
|
+
containerName: string;
|
|
50
|
+
}): Promise<{
|
|
51
|
+
content: [{
|
|
52
|
+
type: "text";
|
|
53
|
+
text: string;
|
|
54
|
+
}];
|
|
55
|
+
}>;
|
|
56
|
+
export declare function handleRestartContainer({ containerName }: {
|
|
57
|
+
containerName: string;
|
|
58
|
+
}): Promise<{
|
|
59
|
+
content: [{
|
|
60
|
+
type: "text";
|
|
61
|
+
text: string;
|
|
62
|
+
}];
|
|
63
|
+
}>;
|
|
64
|
+
export declare function handleOpenWebclient({ containerName }: {
|
|
65
|
+
containerName: string;
|
|
66
|
+
}): Promise<{
|
|
67
|
+
content: [{
|
|
68
|
+
type: "text";
|
|
69
|
+
text: string;
|
|
70
|
+
}];
|
|
71
|
+
}>;
|
|
72
|
+
export declare function handleListApps({ containerName, publisher, }: {
|
|
73
|
+
containerName: string;
|
|
74
|
+
publisher?: string;
|
|
75
|
+
}): Promise<{
|
|
76
|
+
content: [{
|
|
77
|
+
type: "text";
|
|
78
|
+
text: string;
|
|
79
|
+
}];
|
|
80
|
+
}>;
|
|
81
|
+
export declare function handleInstallApp({ containerName, appFile, userName, password, }: {
|
|
82
|
+
containerName: string;
|
|
83
|
+
appFile: string;
|
|
84
|
+
userName: string;
|
|
85
|
+
password: string;
|
|
86
|
+
}): Promise<{
|
|
87
|
+
content: [{
|
|
88
|
+
type: "text";
|
|
89
|
+
text: string;
|
|
90
|
+
}];
|
|
91
|
+
}>;
|
|
92
|
+
export declare function handleUninstallApp({ containerName, appName, appPublisher, }: {
|
|
93
|
+
containerName: string;
|
|
94
|
+
appName: string;
|
|
95
|
+
appPublisher: string;
|
|
96
|
+
}): Promise<{
|
|
97
|
+
content: [{
|
|
98
|
+
type: "text";
|
|
99
|
+
text: string;
|
|
100
|
+
}];
|
|
101
|
+
}>;
|
|
102
|
+
export declare function handlePublishProject({ containerName, projectFolder, userName, password, }: {
|
|
103
|
+
containerName: string;
|
|
104
|
+
projectFolder: string;
|
|
105
|
+
userName: string;
|
|
106
|
+
password: string;
|
|
107
|
+
}): Promise<{
|
|
108
|
+
content: [{
|
|
109
|
+
type: "text";
|
|
110
|
+
text: string;
|
|
111
|
+
}];
|
|
112
|
+
}>;
|
|
113
|
+
export declare function handleImportTestToolkit({ containerName, librariesOnly, userName, password, }: {
|
|
114
|
+
containerName: string;
|
|
115
|
+
librariesOnly: boolean;
|
|
116
|
+
userName: string;
|
|
117
|
+
password: string;
|
|
118
|
+
}): Promise<{
|
|
119
|
+
content: [{
|
|
120
|
+
type: "text";
|
|
121
|
+
text: string;
|
|
122
|
+
}];
|
|
123
|
+
}>;
|
|
124
|
+
export declare function handleImportLicense({ containerName, licenseFile, }: {
|
|
125
|
+
containerName: string;
|
|
126
|
+
licenseFile: string;
|
|
127
|
+
}): Promise<{
|
|
128
|
+
content: [{
|
|
129
|
+
type: "text";
|
|
130
|
+
text: string;
|
|
131
|
+
}];
|
|
132
|
+
}>;
|
|
133
|
+
export declare function handleRunTests({ containerName, testCodeunitId, testFunctionName, appProjectFolder, userName, password, }: {
|
|
134
|
+
containerName: string;
|
|
135
|
+
testCodeunitId?: number;
|
|
136
|
+
testFunctionName?: string;
|
|
137
|
+
appProjectFolder?: string;
|
|
138
|
+
userName: string;
|
|
139
|
+
password: string;
|
|
140
|
+
}): Promise<{
|
|
141
|
+
content: [{
|
|
142
|
+
type: "text";
|
|
143
|
+
text: string;
|
|
144
|
+
}];
|
|
145
|
+
}>;
|