biaws 0.2.1 → 0.3.0
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 +5 -1
- package/docs/command-taxonomy.md +3 -4
- package/docs/instance-lifecycle.md +40 -0
- package/docs/releasing.md +7 -3
- package/package.json +1 -1
- package/src/cli/admin/instance/update.js +1 -0
- package/src/commandFactories.js +12 -2
- package/src/commands/agent.js +91 -46
- package/src/commands/configure/claude.js +9 -3
- package/src/commands/configure/codex.js +9 -3
- package/src/commands/instance/update.js +164 -0
- package/src/commands/instance.js +1 -0
- package/src/configuration/commands.js +1 -1
- package/src/configure/command.js +48 -0
- package/src/core/context.js +9 -10
- package/src/instance/service.js +88 -6
package/README.md
CHANGED
|
@@ -122,6 +122,7 @@ biaws workspace api PATCH /recurso/ID --body '{"status":"Ativo"}'
|
|
|
122
122
|
```bash
|
|
123
123
|
biaws workspace agent configure codex
|
|
124
124
|
biaws workspace agent configure claude
|
|
125
|
+
biaws workspace agent configure codex --no-interactive --env-file /caminho/instancia.env --workspace workspace-id
|
|
125
126
|
biaws workspace agent doctor codex
|
|
126
127
|
biaws workspace skills list
|
|
127
128
|
biaws workspace skills install SKILL_ID
|
|
@@ -131,7 +132,9 @@ biaws workspace skills status
|
|
|
131
132
|
|
|
132
133
|
O Codex usa `.codex/config.toml` e `.agents/skills`. O Claude Code usa
|
|
133
134
|
`.mcp.json` e `.claude/skills`. Configurações de terceiros são preservadas e
|
|
134
|
-
nenhuma credencial é gravada no projeto.
|
|
135
|
+
nenhuma credencial é gravada no projeto. O MCP é configurado como
|
|
136
|
+
`npx --yes biaws-mcp@<versão-fixada>`, sem exigir um checkout local da
|
|
137
|
+
plataforma.
|
|
135
138
|
|
|
136
139
|
## Monitoramento
|
|
137
140
|
|
|
@@ -171,6 +174,7 @@ biaws admin instance list
|
|
|
171
174
|
biaws admin instance status local
|
|
172
175
|
biaws admin instance start local
|
|
173
176
|
biaws admin instance stop local
|
|
177
|
+
biaws admin instance update local
|
|
174
178
|
biaws admin instance backup local
|
|
175
179
|
```
|
|
176
180
|
|
package/docs/command-taxonomy.md
CHANGED
|
@@ -10,7 +10,7 @@ biaws
|
|
|
10
10
|
│ ├── install
|
|
11
11
|
│ ├── doctor
|
|
12
12
|
│ ├── config
|
|
13
|
-
│ ├── instance setup|list|show|status|start|stop|backup|restore|remove
|
|
13
|
+
│ ├── instance setup|list|show|status|start|stop|update|backup|restore|remove
|
|
14
14
|
│ └── monitoring build|validate|start|stop|status|logs|provision
|
|
15
15
|
├── config
|
|
16
16
|
│ ├── init|login|show|set|unset|doctor
|
|
@@ -61,9 +61,8 @@ flags > variáveis de ambiente > configuração da pasta > perfil global > defau
|
|
|
61
61
|
```
|
|
62
62
|
|
|
63
63
|
As variáveis canônicas são `BIAWS_API_URL`, `BIAWS_API_KEY` e
|
|
64
|
-
`BIAWS_WORKSPACE_ID`.
|
|
65
|
-
|
|
66
|
-
antigos do CLI.
|
|
64
|
+
`BIAWS_WORKSPACE_ID`. Variáveis com o prefixo temporário anterior não são
|
|
65
|
+
aceitas como aliases.
|
|
67
66
|
|
|
68
67
|
## Convenções
|
|
69
68
|
|
|
@@ -35,6 +35,46 @@ biaws admin instance stop local
|
|
|
35
35
|
`status` invocam Docker Compose sem shell e com projeto, arquivo Compose e env
|
|
36
36
|
da instância resolvidos explicitamente.
|
|
37
37
|
|
|
38
|
+
## Atualização
|
|
39
|
+
|
|
40
|
+
Depois de atualizar o checkout ou instalar uma nova release, reconstrua API,
|
|
41
|
+
UI e os demais serviços com:
|
|
42
|
+
|
|
43
|
+
```bash
|
|
44
|
+
biaws admin instance update local
|
|
45
|
+
```
|
|
46
|
+
|
|
47
|
+
Antes de alterar a instância, consulte a versão efetivamente implantada e a
|
|
48
|
+
versão disponível no checkout:
|
|
49
|
+
|
|
50
|
+
```bash
|
|
51
|
+
biaws admin instance update local --check
|
|
52
|
+
biaws admin instance update local --check --json
|
|
53
|
+
```
|
|
54
|
+
|
|
55
|
+
O resultado informa `currentVersion`, `newVersion` e `updateRequired`.
|
|
56
|
+
Instâncias criadas antes desse mecanismo começam com `currentVersion` igual a
|
|
57
|
+
`unknown` e são consideradas pendentes. Se as versões forem iguais, o comando
|
|
58
|
+
não cria backup nem reconstrói containers; use `--force` para repetir o deploy.
|
|
59
|
+
|
|
60
|
+
Por padrão, o comando valida o arquivo Compose, solicita uma senha, cria um
|
|
61
|
+
backup completo e então executa `up -d --build --wait`. Em automação, use um
|
|
62
|
+
arquivo privado para a senha:
|
|
63
|
+
|
|
64
|
+
```bash
|
|
65
|
+
biaws admin instance update local --password-file /caminho/privado/senha
|
|
66
|
+
```
|
|
67
|
+
|
|
68
|
+
Se a política operacional já produz um backup externo verificável, a criação
|
|
69
|
+
local pode ser dispensada explicitamente com `--skip-backup`. O update preserva
|
|
70
|
+
o project name, o `.env`, os volumes e os bind mounts da instância. Ele não
|
|
71
|
+
executa `git pull` nem troca a versão do checkout.
|
|
72
|
+
|
|
73
|
+
A versão implantada só é registrada depois que `up -d --build --wait` termina
|
|
74
|
+
com sucesso. Ela também é exposta pelo `/api/health` e pelos labels dos
|
|
75
|
+
containers de API e UI. A versão nova é lida do `biaws-cli/package.json` da
|
|
76
|
+
raiz selecionada por `--root`/`BIAWS_ROOT`.
|
|
77
|
+
|
|
38
78
|
## Smoke test Docker real
|
|
39
79
|
|
|
40
80
|
Em um checkout descartável com Docker e OpenSSL disponíveis:
|
package/docs/releasing.md
CHANGED
|
@@ -69,9 +69,13 @@ não são aliases circulares. A remoção dos wrappers públicos só pode ocorre
|
|
|
69
69
|
após aviso de depreciação em uma release `0.x`, telemetria ou evidência de
|
|
70
70
|
adoção e um runbook de substituição atualizado.
|
|
71
71
|
|
|
72
|
-
O pacote npm isolado cobre ajuda
|
|
73
|
-
|
|
74
|
-
|
|
72
|
+
O pacote npm isolado cobre ajuda, API remota, instalação das skills e
|
|
73
|
+
configuração do MCP publicado. Operações de instância e publicação do catálogo
|
|
74
|
+
inicial dependem do checkout completo; defina `BIAWS_ROOT` para sua raiz quando
|
|
75
|
+
o executável global for chamado fora dela.
|
|
76
|
+
|
|
77
|
+
Publique primeiro a versão de `biaws-mcp` fixada pelo CLI. O CLI nunca deve ser
|
|
78
|
+
publicado apontando para uma versão do MCP que ainda não exista no registry.
|
|
75
79
|
|
|
76
80
|
## Exercício de release
|
|
77
81
|
|
package/package.json
CHANGED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export { default } from "../../../commands/instance/update.js";
|
package/src/commandFactories.js
CHANGED
|
@@ -4,6 +4,7 @@ import { AuthenticatedApiCommand, ProjectCommand } from "./baseCommands.js";
|
|
|
4
4
|
import {
|
|
5
5
|
configureContextFlags,
|
|
6
6
|
configureContextInput,
|
|
7
|
+
interactiveConfigureInput,
|
|
7
8
|
legacyAgentContext,
|
|
8
9
|
} from "./configure/command.js";
|
|
9
10
|
import { runAgentCommand } from "./commands/agent.js";
|
|
@@ -85,12 +86,21 @@ export function createAgentCommand(action) {
|
|
|
85
86
|
|
|
86
87
|
async run() {
|
|
87
88
|
const { args, flags } = await this.parse(this.constructor);
|
|
88
|
-
const
|
|
89
|
+
const input =
|
|
90
|
+
action === "configure"
|
|
91
|
+
? await interactiveConfigureInput(this, flags)
|
|
92
|
+
: configureContextInput(flags);
|
|
93
|
+
const context = await this.projectContext(input);
|
|
89
94
|
await runAgentCommand(
|
|
90
95
|
context.api,
|
|
91
96
|
action,
|
|
92
97
|
[args.client],
|
|
93
|
-
{
|
|
98
|
+
{
|
|
99
|
+
...flags,
|
|
100
|
+
interactive: input.interactive,
|
|
101
|
+
project: context.projectDirectory,
|
|
102
|
+
prompts: this.adapters.prompts,
|
|
103
|
+
},
|
|
94
104
|
legacyAgentContext(context),
|
|
95
105
|
);
|
|
96
106
|
}
|
package/src/commands/agent.js
CHANGED
|
@@ -4,11 +4,17 @@ import path from "node:path";
|
|
|
4
4
|
|
|
5
5
|
import { BaseCommand, TOOL_DIRECTORY } from "../baseCommands.js";
|
|
6
6
|
import { showTopic } from "../commandFactories.js";
|
|
7
|
+
import { CliError } from "../core/errors.js";
|
|
7
8
|
import { readLock } from "../localSkills.js";
|
|
8
9
|
import { runSkillsCommand } from "./skills.js";
|
|
9
10
|
|
|
10
11
|
const CODEX_BEGIN = "# BEGIN BIAWS MANAGED MCP";
|
|
11
12
|
const CODEX_END = "# END BIAWS MANAGED MCP";
|
|
13
|
+
export const MCP_PACKAGE_NAME = "biaws-mcp";
|
|
14
|
+
export const MCP_PACKAGE_VERSION = "0.2.0";
|
|
15
|
+
export const MCP_PACKAGE_SPEC = `${MCP_PACKAGE_NAME}@${MCP_PACKAGE_VERSION}`;
|
|
16
|
+
const MCP_COMMAND = "npx";
|
|
17
|
+
const MCP_ARGS = ["--yes", MCP_PACKAGE_SPEC];
|
|
12
18
|
|
|
13
19
|
function skillTarget(client, projectDirectory) {
|
|
14
20
|
return path.join(
|
|
@@ -18,8 +24,18 @@ function skillTarget(client, projectDirectory) {
|
|
|
18
24
|
);
|
|
19
25
|
}
|
|
20
26
|
|
|
21
|
-
function
|
|
22
|
-
|
|
27
|
+
function isExpectedClaudeServer(server) {
|
|
28
|
+
if (
|
|
29
|
+
server?.command === MCP_COMMAND &&
|
|
30
|
+
JSON.stringify(server.args || []) === JSON.stringify(MCP_ARGS)
|
|
31
|
+
) {
|
|
32
|
+
return true;
|
|
33
|
+
}
|
|
34
|
+
return (
|
|
35
|
+
server?.command === "node" &&
|
|
36
|
+
server.args?.length === 1 &&
|
|
37
|
+
/(?:^|[\\/])biaws-mcp[\\/]src[\\/]index\.js$/u.test(server.args[0])
|
|
38
|
+
);
|
|
23
39
|
}
|
|
24
40
|
|
|
25
41
|
async function readOptional(filePath, fallback = "") {
|
|
@@ -33,7 +49,6 @@ async function readOptional(filePath, fallback = "") {
|
|
|
33
49
|
|
|
34
50
|
async function writeCodexConfiguration(
|
|
35
51
|
projectDirectory,
|
|
36
|
-
entrypoint,
|
|
37
52
|
envFile,
|
|
38
53
|
workspaceId,
|
|
39
54
|
force,
|
|
@@ -59,9 +74,9 @@ async function writeCodexConfiguration(
|
|
|
59
74
|
}
|
|
60
75
|
const managed = `${CODEX_BEGIN}
|
|
61
76
|
[mcp_servers.biaws]
|
|
62
|
-
command =
|
|
63
|
-
args =
|
|
64
|
-
env = {${envFile ? ` BIAWS_ENV_FILE = ${JSON.stringify(envFile)},` : ""}
|
|
77
|
+
command = ${JSON.stringify(MCP_COMMAND)}
|
|
78
|
+
args = ${JSON.stringify(MCP_ARGS)}
|
|
79
|
+
env = {${envFile ? ` BIAWS_ENV_FILE = ${JSON.stringify(envFile)},` : ""} BIAWS_WORKSPACE_ID = ${JSON.stringify(workspaceId)} }
|
|
65
80
|
${CODEX_END}
|
|
66
81
|
`;
|
|
67
82
|
const output = current ? `${current}\n\n${managed}` : managed;
|
|
@@ -71,7 +86,6 @@ ${CODEX_END}
|
|
|
71
86
|
|
|
72
87
|
async function writeClaudeConfiguration(
|
|
73
88
|
projectDirectory,
|
|
74
|
-
entrypoint,
|
|
75
89
|
envFile,
|
|
76
90
|
workspaceId,
|
|
77
91
|
force,
|
|
@@ -85,31 +99,34 @@ async function writeClaudeConfiguration(
|
|
|
85
99
|
throw new Error(`JSON inválido em ${configPath}`);
|
|
86
100
|
}
|
|
87
101
|
config.mcpServers ||= {};
|
|
88
|
-
if (
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
);
|
|
97
|
-
}
|
|
102
|
+
if (
|
|
103
|
+
config.mcpServers.biaws &&
|
|
104
|
+
!force &&
|
|
105
|
+
!isExpectedClaudeServer(config.mcpServers.biaws)
|
|
106
|
+
) {
|
|
107
|
+
throw new Error(
|
|
108
|
+
`Já existe uma configuração Claude diferente para o servidor biaws em ${configPath}; use --force para substituí-la`,
|
|
109
|
+
);
|
|
98
110
|
}
|
|
99
111
|
config.mcpServers.biaws = {
|
|
100
112
|
type: "stdio",
|
|
101
|
-
command:
|
|
102
|
-
args:
|
|
113
|
+
command: MCP_COMMAND,
|
|
114
|
+
args: MCP_ARGS,
|
|
103
115
|
env: {
|
|
104
116
|
...(envFile ? { BIAWS_ENV_FILE: envFile } : {}),
|
|
105
|
-
|
|
117
|
+
BIAWS_WORKSPACE_ID: workspaceId,
|
|
106
118
|
},
|
|
107
119
|
};
|
|
108
120
|
await writeFile(configPath, `${JSON.stringify(config, null, 2)}\n`, "utf8");
|
|
109
121
|
return configPath;
|
|
110
122
|
}
|
|
111
123
|
|
|
112
|
-
async function resolveWorkspaceId(
|
|
124
|
+
async function resolveWorkspaceId(
|
|
125
|
+
api,
|
|
126
|
+
requestedWorkspaceId = "",
|
|
127
|
+
interactive = false,
|
|
128
|
+
prompts,
|
|
129
|
+
) {
|
|
113
130
|
let identity;
|
|
114
131
|
try {
|
|
115
132
|
identity = await api.identity();
|
|
@@ -121,7 +138,22 @@ async function resolveWorkspaceId(api, requestedWorkspaceId = "") {
|
|
|
121
138
|
}
|
|
122
139
|
throw error;
|
|
123
140
|
}
|
|
124
|
-
const
|
|
141
|
+
const workspaces = identity?.actor?.workspaces || [];
|
|
142
|
+
let workspaceId = requestedWorkspaceId || identity?.actor?.workspaceId;
|
|
143
|
+
if (interactive && workspaces.length > 0) {
|
|
144
|
+
workspaceId = await prompts.ask({
|
|
145
|
+
name: "workspaceId",
|
|
146
|
+
type: "select",
|
|
147
|
+
message: "Workspace do projeto",
|
|
148
|
+
choices: workspaces.map((workspace) => ({
|
|
149
|
+
name: workspace.name
|
|
150
|
+
? `${workspace.name} (${workspace.id})`
|
|
151
|
+
: workspace.id,
|
|
152
|
+
value: workspace.id,
|
|
153
|
+
})),
|
|
154
|
+
...(workspaceId ? { default: workspaceId } : {}),
|
|
155
|
+
});
|
|
156
|
+
}
|
|
125
157
|
if (!workspaceId) {
|
|
126
158
|
if ((identity?.actor?.workspaces?.length || 0) > 1) {
|
|
127
159
|
throw new Error(
|
|
@@ -142,21 +174,40 @@ async function configure(api, client, options, context) {
|
|
|
142
174
|
);
|
|
143
175
|
}
|
|
144
176
|
const projectDirectory = path.resolve(options.project || process.cwd());
|
|
145
|
-
const { workspaceId } = await resolveWorkspaceId(
|
|
177
|
+
const { workspaceId } = await resolveWorkspaceId(
|
|
178
|
+
api,
|
|
179
|
+
context.workspaceId,
|
|
180
|
+
options.interactive,
|
|
181
|
+
options.prompts,
|
|
182
|
+
);
|
|
183
|
+
if (options.interactive) {
|
|
184
|
+
const confirmed = await options.prompts.ask({
|
|
185
|
+
name: "confirmConfiguration",
|
|
186
|
+
type: "confirm",
|
|
187
|
+
message: `Configurar ${client} em ${projectDirectory} para o workspace ${workspaceId}?`,
|
|
188
|
+
default: true,
|
|
189
|
+
});
|
|
190
|
+
if (!confirmed) {
|
|
191
|
+
throw new CliError(
|
|
192
|
+
"Configuração cancelada antes de qualquer alteração.",
|
|
193
|
+
{
|
|
194
|
+
code: "PROMPT_CANCELLED",
|
|
195
|
+
exitCode: 130,
|
|
196
|
+
},
|
|
197
|
+
);
|
|
198
|
+
}
|
|
199
|
+
}
|
|
146
200
|
const target = skillTarget(client, projectDirectory);
|
|
147
|
-
const entrypoint = mcpEntrypoint(context.toolDirectory);
|
|
148
201
|
const configPath =
|
|
149
202
|
client === "codex"
|
|
150
203
|
? await writeCodexConfiguration(
|
|
151
204
|
projectDirectory,
|
|
152
|
-
entrypoint,
|
|
153
205
|
context.envFile,
|
|
154
206
|
workspaceId,
|
|
155
207
|
options.force,
|
|
156
208
|
)
|
|
157
209
|
: await writeClaudeConfiguration(
|
|
158
210
|
projectDirectory,
|
|
159
|
-
entrypoint,
|
|
160
211
|
context.envFile,
|
|
161
212
|
workspaceId,
|
|
162
213
|
options.force,
|
|
@@ -173,6 +224,7 @@ async function configure(api, client, options, context) {
|
|
|
173
224
|
configPath,
|
|
174
225
|
skillTarget: target,
|
|
175
226
|
installation,
|
|
227
|
+
mcpPackage: MCP_PACKAGE_SPEC,
|
|
176
228
|
workspaceId,
|
|
177
229
|
};
|
|
178
230
|
if (options.json) console.log(JSON.stringify(result, null, 2));
|
|
@@ -197,14 +249,14 @@ async function requestStatus(url, headers = {}) {
|
|
|
197
249
|
}
|
|
198
250
|
}
|
|
199
251
|
|
|
200
|
-
async function mcpStatus(
|
|
252
|
+
async function mcpStatus(envFile, workspaceId) {
|
|
201
253
|
return new Promise((resolve) => {
|
|
202
|
-
const child = spawn(
|
|
254
|
+
const child = spawn(MCP_COMMAND, MCP_ARGS, {
|
|
203
255
|
stdio: ["pipe", "pipe", "pipe"],
|
|
204
256
|
env: {
|
|
205
257
|
...process.env,
|
|
206
258
|
...(envFile ? { BIAWS_ENV_FILE: envFile } : {}),
|
|
207
|
-
|
|
259
|
+
BIAWS_WORKSPACE_ID: workspaceId,
|
|
208
260
|
},
|
|
209
261
|
});
|
|
210
262
|
let output = "";
|
|
@@ -222,9 +274,9 @@ async function mcpStatus(entrypoint, envFile, workspaceId) {
|
|
|
222
274
|
() =>
|
|
223
275
|
finish({
|
|
224
276
|
ok: false,
|
|
225
|
-
detail: errorOutput.trim() || "MCP não respondeu em
|
|
277
|
+
detail: errorOutput.trim() || "MCP não respondeu em 30 segundos",
|
|
226
278
|
}),
|
|
227
|
-
|
|
279
|
+
30_000,
|
|
228
280
|
);
|
|
229
281
|
child.stderr.on("data", (chunk) => {
|
|
230
282
|
errorOutput += chunk;
|
|
@@ -283,22 +335,17 @@ async function mcpStatus(entrypoint, envFile, workspaceId) {
|
|
|
283
335
|
});
|
|
284
336
|
}
|
|
285
337
|
|
|
286
|
-
function configurationStatus(
|
|
287
|
-
client,
|
|
288
|
-
contents,
|
|
289
|
-
entrypoint,
|
|
290
|
-
envFile,
|
|
291
|
-
workspaceId,
|
|
292
|
-
) {
|
|
338
|
+
function configurationStatus(client, contents, envFile, workspaceId) {
|
|
293
339
|
if (typeof contents !== "string") {
|
|
294
340
|
return { ok: false, detail: "Configuração MCP não encontrada" };
|
|
295
341
|
}
|
|
296
342
|
if (client === "codex") {
|
|
297
343
|
const ok =
|
|
298
344
|
contents.includes("[mcp_servers.biaws]") &&
|
|
299
|
-
contents.includes(JSON.stringify(
|
|
345
|
+
contents.includes(`command = ${JSON.stringify(MCP_COMMAND)}`) &&
|
|
346
|
+
contents.includes(`args = ${JSON.stringify(MCP_ARGS)}`) &&
|
|
300
347
|
(!envFile || contents.includes(JSON.stringify(envFile))) &&
|
|
301
|
-
contents.includes(`
|
|
348
|
+
contents.includes(`BIAWS_WORKSPACE_ID = ${JSON.stringify(workspaceId)}`);
|
|
302
349
|
return {
|
|
303
350
|
ok,
|
|
304
351
|
detail: ok
|
|
@@ -309,10 +356,10 @@ function configurationStatus(
|
|
|
309
356
|
try {
|
|
310
357
|
const server = JSON.parse(contents).mcpServers?.biaws;
|
|
311
358
|
const ok =
|
|
312
|
-
server?.command ===
|
|
313
|
-
JSON.stringify(server.args || []) === JSON.stringify(
|
|
359
|
+
server?.command === MCP_COMMAND &&
|
|
360
|
+
JSON.stringify(server.args || []) === JSON.stringify(MCP_ARGS) &&
|
|
314
361
|
(!envFile || server.env?.BIAWS_ENV_FILE === envFile) &&
|
|
315
|
-
server.env?.
|
|
362
|
+
server.env?.BIAWS_WORKSPACE_ID === workspaceId;
|
|
316
363
|
return {
|
|
317
364
|
ok,
|
|
318
365
|
detail: ok
|
|
@@ -336,11 +383,10 @@ async function doctor(api, client, options, context) {
|
|
|
336
383
|
client === "codex"
|
|
337
384
|
? path.join(projectDirectory, ".codex", "config.toml")
|
|
338
385
|
: path.join(projectDirectory, ".mcp.json");
|
|
339
|
-
const entrypoint = mcpEntrypoint(context.toolDirectory);
|
|
340
386
|
const resolved = await resolveWorkspaceId(api, context.workspaceId);
|
|
341
387
|
const [health, mcp, configContents, lock] = await Promise.all([
|
|
342
388
|
requestStatus(`${context.apiUrl}/api/health`),
|
|
343
|
-
mcpStatus(
|
|
389
|
+
mcpStatus(context.envFile, resolved.workspaceId),
|
|
344
390
|
readOptional(configPath, null),
|
|
345
391
|
readLock(target),
|
|
346
392
|
]);
|
|
@@ -354,7 +400,6 @@ async function doctor(api, client, options, context) {
|
|
|
354
400
|
...configurationStatus(
|
|
355
401
|
client,
|
|
356
402
|
configContents,
|
|
357
|
-
entrypoint,
|
|
358
403
|
context.envFile,
|
|
359
404
|
resolved.workspaceId,
|
|
360
405
|
),
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { ProjectCommand } from "../../baseCommands.js";
|
|
2
2
|
import {
|
|
3
3
|
configureContextFlags,
|
|
4
|
-
|
|
4
|
+
interactiveConfigureInput,
|
|
5
5
|
legacyAgentContext,
|
|
6
6
|
} from "../../configure/command.js";
|
|
7
7
|
import { runAgentCommand } from "../agent.js";
|
|
@@ -13,12 +13,18 @@ export default class ConfigureClaude extends ProjectCommand {
|
|
|
13
13
|
|
|
14
14
|
async run() {
|
|
15
15
|
const { flags } = await this.parse(ConfigureClaude);
|
|
16
|
-
const
|
|
16
|
+
const input = await interactiveConfigureInput(this, flags);
|
|
17
|
+
const context = await this.projectContext(input);
|
|
17
18
|
await runAgentCommand(
|
|
18
19
|
context.api,
|
|
19
20
|
"configure",
|
|
20
21
|
["claude"],
|
|
21
|
-
{
|
|
22
|
+
{
|
|
23
|
+
...flags,
|
|
24
|
+
interactive: input.interactive,
|
|
25
|
+
project: context.projectDirectory,
|
|
26
|
+
prompts: this.adapters.prompts,
|
|
27
|
+
},
|
|
22
28
|
legacyAgentContext(context),
|
|
23
29
|
);
|
|
24
30
|
}
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { ProjectCommand } from "../../baseCommands.js";
|
|
2
2
|
import {
|
|
3
3
|
configureContextFlags,
|
|
4
|
-
|
|
4
|
+
interactiveConfigureInput,
|
|
5
5
|
legacyAgentContext,
|
|
6
6
|
} from "../../configure/command.js";
|
|
7
7
|
import { runAgentCommand } from "../agent.js";
|
|
@@ -13,12 +13,18 @@ export default class ConfigureCodex extends ProjectCommand {
|
|
|
13
13
|
|
|
14
14
|
async run() {
|
|
15
15
|
const { flags } = await this.parse(ConfigureCodex);
|
|
16
|
-
const
|
|
16
|
+
const input = await interactiveConfigureInput(this, flags);
|
|
17
|
+
const context = await this.projectContext(input);
|
|
17
18
|
await runAgentCommand(
|
|
18
19
|
context.api,
|
|
19
20
|
"configure",
|
|
20
21
|
["codex"],
|
|
21
|
-
{
|
|
22
|
+
{
|
|
23
|
+
...flags,
|
|
24
|
+
interactive: input.interactive,
|
|
25
|
+
project: context.projectDirectory,
|
|
26
|
+
prompts: this.adapters.prompts,
|
|
27
|
+
},
|
|
22
28
|
legacyAgentContext(context),
|
|
23
29
|
);
|
|
24
30
|
}
|
|
@@ -0,0 +1,164 @@
|
|
|
1
|
+
import { Flags } from "@oclif/core";
|
|
2
|
+
|
|
3
|
+
import { LocalInstanceCommand } from "../../baseCommands.js";
|
|
4
|
+
import {
|
|
5
|
+
contextFlags,
|
|
6
|
+
contextInput,
|
|
7
|
+
instanceArgument,
|
|
8
|
+
} from "../../instance/command.js";
|
|
9
|
+
import {
|
|
10
|
+
backupArguments,
|
|
11
|
+
executeInstanceScript,
|
|
12
|
+
getInstance,
|
|
13
|
+
getInstanceUpdateStatus,
|
|
14
|
+
updateInstance,
|
|
15
|
+
validateInstanceUpdate,
|
|
16
|
+
withPasswordFile,
|
|
17
|
+
} from "../../instance/service.js";
|
|
18
|
+
|
|
19
|
+
function versionCheckResult(instance, versionStatus, json) {
|
|
20
|
+
if (json) return versionStatus;
|
|
21
|
+
return [
|
|
22
|
+
`Instância: ${instance.name}`,
|
|
23
|
+
`Versão atual: ${versionStatus.currentVersion}`,
|
|
24
|
+
`Nova versão: ${versionStatus.newVersion}`,
|
|
25
|
+
`Atualização necessária: ${versionStatus.updateRequired ? "sim" : "não"}`,
|
|
26
|
+
].join("\n");
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
function unchangedResult(instance, versionStatus, json) {
|
|
30
|
+
return json
|
|
31
|
+
? { ...versionStatus, updated: false, backupCreated: false }
|
|
32
|
+
: `Instância ${instance.name} já está na versão ${versionStatus.newVersion}.`;
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
async function createUpdateBackup(command, instance, context, flags) {
|
|
36
|
+
if (flags["skip-backup"]) return "";
|
|
37
|
+
|
|
38
|
+
let password;
|
|
39
|
+
if (!flags["password-file"]) {
|
|
40
|
+
if (!context.isInteractive) {
|
|
41
|
+
command.error(
|
|
42
|
+
"Atualização exige --password-file ou --skip-backup em modo não interativo.",
|
|
43
|
+
{ code: "BACKUP_PASSWORD_REQUIRED", exit: 2 },
|
|
44
|
+
);
|
|
45
|
+
}
|
|
46
|
+
password = await command.adapters.prompts.ask({
|
|
47
|
+
name: "password",
|
|
48
|
+
type: "password",
|
|
49
|
+
message: "Senha do backup anterior à atualização",
|
|
50
|
+
});
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
return withPasswordFile(
|
|
54
|
+
command.adapters.filesystem,
|
|
55
|
+
password,
|
|
56
|
+
flags["password-file"],
|
|
57
|
+
async (passwordFile) =>
|
|
58
|
+
executeInstanceScript(
|
|
59
|
+
backupArguments(instance, context, {
|
|
60
|
+
output: flags["backup-output"],
|
|
61
|
+
passwordFile,
|
|
62
|
+
}),
|
|
63
|
+
context,
|
|
64
|
+
command.adapters.processRunner,
|
|
65
|
+
{ secrets: password ? [password] : [], silent: flags.json },
|
|
66
|
+
),
|
|
67
|
+
);
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
function updatedResult(instance, versionStatus, flags, backupOutput) {
|
|
71
|
+
if (!flags.json) {
|
|
72
|
+
return `Instância ${instance.name} atualizada de ${versionStatus.currentVersion} para ${versionStatus.newVersion}. UI: ${instance.publicUrl}`;
|
|
73
|
+
}
|
|
74
|
+
return {
|
|
75
|
+
instance: instance.name,
|
|
76
|
+
operation: "update",
|
|
77
|
+
currentVersion: versionStatus.currentVersion,
|
|
78
|
+
newVersion: versionStatus.newVersion,
|
|
79
|
+
updateRequired: versionStatus.updateRequired,
|
|
80
|
+
updated: true,
|
|
81
|
+
backupCreated: !flags["skip-backup"],
|
|
82
|
+
backupOutput,
|
|
83
|
+
ui: instance.publicUrl,
|
|
84
|
+
};
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
export default class InstanceUpdate extends LocalInstanceCommand {
|
|
88
|
+
static description =
|
|
89
|
+
"reconstrói e atualiza os serviços preservando os dados da instância";
|
|
90
|
+
static args = { instance: instanceArgument };
|
|
91
|
+
static flags = {
|
|
92
|
+
...contextFlags,
|
|
93
|
+
"backup-output": Flags.string({
|
|
94
|
+
description: "arquivo de destino do backup anterior à atualização",
|
|
95
|
+
}),
|
|
96
|
+
"password-file": Flags.string({
|
|
97
|
+
description: "arquivo privado contendo a senha do backup",
|
|
98
|
+
}),
|
|
99
|
+
"skip-backup": Flags.boolean({
|
|
100
|
+
description: "atualiza sem criar o backup de segurança",
|
|
101
|
+
exclusive: ["backup-output", "password-file"],
|
|
102
|
+
}),
|
|
103
|
+
check: Flags.boolean({
|
|
104
|
+
description: "compara a versão instalada com a versão da release",
|
|
105
|
+
exclusive: ["backup-output", "password-file", "skip-backup", "force"],
|
|
106
|
+
}),
|
|
107
|
+
force: Flags.boolean({
|
|
108
|
+
description: "reconstrói mesmo quando a instância já está na versão nova",
|
|
109
|
+
exclusive: ["check"],
|
|
110
|
+
}),
|
|
111
|
+
json: Flags.boolean({ description: "emite resultado JSON" }),
|
|
112
|
+
};
|
|
113
|
+
|
|
114
|
+
async run() {
|
|
115
|
+
const { args, flags } = await this.parse(InstanceUpdate);
|
|
116
|
+
const context = await this.localContext(contextInput(flags, args.instance));
|
|
117
|
+
const instance = await getInstance(
|
|
118
|
+
context,
|
|
119
|
+
this.adapters.filesystem,
|
|
120
|
+
args.instance,
|
|
121
|
+
);
|
|
122
|
+
const versionStatus = await getInstanceUpdateStatus(
|
|
123
|
+
instance,
|
|
124
|
+
context,
|
|
125
|
+
this.adapters.filesystem,
|
|
126
|
+
);
|
|
127
|
+
if (flags.check) {
|
|
128
|
+
this.output({ json: flags.json }).result(
|
|
129
|
+
versionCheckResult(instance, versionStatus, flags.json),
|
|
130
|
+
);
|
|
131
|
+
return;
|
|
132
|
+
}
|
|
133
|
+
if (!versionStatus.updateRequired && !flags.force) {
|
|
134
|
+
this.output({ json: flags.json }).result(
|
|
135
|
+
unchangedResult(instance, versionStatus, flags.json),
|
|
136
|
+
);
|
|
137
|
+
return;
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
await validateInstanceUpdate(
|
|
141
|
+
instance,
|
|
142
|
+
context,
|
|
143
|
+
this.adapters.processRunner,
|
|
144
|
+
);
|
|
145
|
+
const backupOutput = await createUpdateBackup(
|
|
146
|
+
this,
|
|
147
|
+
instance,
|
|
148
|
+
context,
|
|
149
|
+
flags,
|
|
150
|
+
);
|
|
151
|
+
|
|
152
|
+
await updateInstance(
|
|
153
|
+
instance,
|
|
154
|
+
context,
|
|
155
|
+
this.adapters.filesystem,
|
|
156
|
+
this.adapters.processRunner,
|
|
157
|
+
versionStatus.newVersion,
|
|
158
|
+
this.adapters.environment,
|
|
159
|
+
);
|
|
160
|
+
this.output({ json: flags.json }).result(
|
|
161
|
+
updatedResult(instance, versionStatus, flags, backupOutput),
|
|
162
|
+
);
|
|
163
|
+
}
|
|
164
|
+
}
|
package/src/commands/instance.js
CHANGED
|
@@ -52,7 +52,7 @@ function credentialsWithKey(configuration, name, apiKey) {
|
|
|
52
52
|
async function requiredApiKey(command) {
|
|
53
53
|
const fromEnvironment = String(
|
|
54
54
|
command.adapters.environment.BIAWS_API_KEY ||
|
|
55
|
-
command.adapters.environment.
|
|
55
|
+
command.adapters.environment.BIAWS_API_KEY ||
|
|
56
56
|
"",
|
|
57
57
|
).trim();
|
|
58
58
|
if (fromEnvironment) return fromEnvironment;
|
package/src/configure/command.js
CHANGED
|
@@ -2,6 +2,8 @@ import path from "node:path";
|
|
|
2
2
|
|
|
3
3
|
import { Args, Flags } from "@oclif/core";
|
|
4
4
|
|
|
5
|
+
import { CliError } from "../core/errors.js";
|
|
6
|
+
|
|
5
7
|
export const configureContextFlags = Object.freeze({
|
|
6
8
|
project: Flags.string({
|
|
7
9
|
char: "p",
|
|
@@ -16,6 +18,12 @@ export const configureContextFlags = Object.freeze({
|
|
|
16
18
|
"env-file": Flags.string({
|
|
17
19
|
description: "arquivo privado com URL e chave de API",
|
|
18
20
|
}),
|
|
21
|
+
interactive: Flags.boolean({
|
|
22
|
+
char: "i",
|
|
23
|
+
allowNo: true,
|
|
24
|
+
description:
|
|
25
|
+
"usa o modo assistente (padrão em terminais; use --no-interactive para desativar)",
|
|
26
|
+
}),
|
|
19
27
|
force: Flags.boolean({
|
|
20
28
|
char: "f",
|
|
21
29
|
description: "substitui somente a configuração BIAWS conflitante",
|
|
@@ -23,6 +31,46 @@ export const configureContextFlags = Object.freeze({
|
|
|
23
31
|
json: Flags.boolean({ description: "emite somente JSON em stdout" }),
|
|
24
32
|
});
|
|
25
33
|
|
|
34
|
+
export async function interactiveConfigureInput(command, flags) {
|
|
35
|
+
const interactive =
|
|
36
|
+
flags.interactive === true ||
|
|
37
|
+
(flags.interactive !== false && command.adapters.terminal.isInteractive);
|
|
38
|
+
if (!interactive) {
|
|
39
|
+
return { ...configureContextInput(flags), interactive: false };
|
|
40
|
+
}
|
|
41
|
+
if (!command.adapters.prompts.isInteractive) {
|
|
42
|
+
throw new CliError("O modo assistente exige um terminal interativo.", {
|
|
43
|
+
code: "INTERACTIVE_INPUT_UNAVAILABLE",
|
|
44
|
+
exitCode: 2,
|
|
45
|
+
});
|
|
46
|
+
}
|
|
47
|
+
const project = String(
|
|
48
|
+
await command.adapters.prompts.ask({
|
|
49
|
+
name: "project",
|
|
50
|
+
message: "Diretório do projeto",
|
|
51
|
+
default: flags.project || command.adapters.cwd(),
|
|
52
|
+
}),
|
|
53
|
+
).trim();
|
|
54
|
+
const envFile = String(
|
|
55
|
+
await command.adapters.prompts.ask({
|
|
56
|
+
name: "envFile",
|
|
57
|
+
message: "Arquivo privado da instância (BIAWS_ENV_FILE)",
|
|
58
|
+
default:
|
|
59
|
+
flags["env-file"] || command.adapters.environment.BIAWS_ENV_FILE || "",
|
|
60
|
+
}),
|
|
61
|
+
).trim();
|
|
62
|
+
if (!envFile) {
|
|
63
|
+
throw new CliError("Informe o arquivo privado da instância.", {
|
|
64
|
+
code: "ENV_FILE_REQUIRED",
|
|
65
|
+
exitCode: 2,
|
|
66
|
+
});
|
|
67
|
+
}
|
|
68
|
+
return {
|
|
69
|
+
...configureContextInput({ ...flags, project, "env-file": envFile }),
|
|
70
|
+
interactive: true,
|
|
71
|
+
};
|
|
72
|
+
}
|
|
73
|
+
|
|
26
74
|
export const clientArgument = Args.string({
|
|
27
75
|
description: "cliente a diagnosticar",
|
|
28
76
|
options: ["codex", "claude"],
|
package/src/core/context.js
CHANGED
|
@@ -126,32 +126,31 @@ export async function resolveCommandContext(options) {
|
|
|
126
126
|
const configuredApiUrl =
|
|
127
127
|
input.apiUrl ||
|
|
128
128
|
explicitFileEnv.BIAWS_API_URL ||
|
|
129
|
-
explicitFileEnv.
|
|
129
|
+
explicitFileEnv.BIAWS_API_URL ||
|
|
130
|
+
environment.BIAWS_API_URL ||
|
|
130
131
|
environment.BIAWS_API_URL ||
|
|
131
|
-
environment.
|
|
132
|
-
environment.ISSUE_API_BASE_URL ||
|
|
132
|
+
environment.BIAWS_API_BASE_URL ||
|
|
133
133
|
workspaceConfiguration.apiUrl ||
|
|
134
134
|
profile.config.apiUrl ||
|
|
135
|
-
effectiveEnv.
|
|
136
|
-
effectiveEnv.
|
|
135
|
+
effectiveEnv.BIAWS_API_URL ||
|
|
136
|
+
effectiveEnv.BIAWS_API_BASE_URL ||
|
|
137
137
|
DEFAULT_API_URL;
|
|
138
138
|
const configuredApiKey = String(
|
|
139
139
|
input.apiKey ||
|
|
140
140
|
explicitFileEnv.BIAWS_API_KEY ||
|
|
141
|
-
explicitFileEnv.
|
|
141
|
+
explicitFileEnv.BIAWS_API_KEY ||
|
|
142
|
+
environment.BIAWS_API_KEY ||
|
|
142
143
|
environment.BIAWS_API_KEY ||
|
|
143
|
-
environment.ISSUE_API_KEY ||
|
|
144
144
|
profile.credentials.apiKey ||
|
|
145
|
-
effectiveEnv.
|
|
145
|
+
effectiveEnv.BIAWS_API_KEY ||
|
|
146
146
|
"",
|
|
147
147
|
);
|
|
148
148
|
const configuredWorkspaceId = String(
|
|
149
149
|
input.workspaceId ||
|
|
150
150
|
input.workspace ||
|
|
151
151
|
environment.BIAWS_WORKSPACE_ID ||
|
|
152
|
-
environment.ISSUE_WORKSPACE_ID ||
|
|
153
152
|
workspaceConfiguration.workspaceId ||
|
|
154
|
-
effectiveEnv.
|
|
153
|
+
effectiveEnv.BIAWS_WORKSPACE_ID ||
|
|
155
154
|
"",
|
|
156
155
|
).trim();
|
|
157
156
|
|
package/src/instance/service.js
CHANGED
|
@@ -6,8 +6,8 @@ import { parseEnv } from "../core/context.js";
|
|
|
6
6
|
|
|
7
7
|
const PORT_FIELDS = Object.freeze({
|
|
8
8
|
mongoPort: "MONGO_PORT",
|
|
9
|
-
apiPort: "
|
|
10
|
-
uiPort: "
|
|
9
|
+
apiPort: "BIAWS_API_PORT",
|
|
10
|
+
uiPort: "BIAWS_UI_PORT",
|
|
11
11
|
});
|
|
12
12
|
|
|
13
13
|
const STORAGE_FIELDS = Object.freeze({
|
|
@@ -18,6 +18,8 @@ const STORAGE_FIELDS = Object.freeze({
|
|
|
18
18
|
secretPath: "BIAWS_SECRET_FILES_PATH",
|
|
19
19
|
});
|
|
20
20
|
|
|
21
|
+
const UNKNOWN_VERSION = "unknown";
|
|
22
|
+
|
|
21
23
|
function usageError(message, code, details) {
|
|
22
24
|
throw new CliError(message, { code, details, exitCode: 2 });
|
|
23
25
|
}
|
|
@@ -141,11 +143,11 @@ export async function listInstances(context, filesystem) {
|
|
|
141
143
|
directory,
|
|
142
144
|
envFile: path.join(directory, ".env"),
|
|
143
145
|
mongoPort: Number(env.MONGO_PORT || 27_017),
|
|
144
|
-
apiPort: Number(env.
|
|
145
|
-
uiPort: Number(env.
|
|
146
|
+
apiPort: Number(env.BIAWS_API_PORT || 3_100),
|
|
147
|
+
uiPort: Number(env.BIAWS_UI_PORT || 4_400),
|
|
146
148
|
publicUrl:
|
|
147
149
|
env.BIAWS_PUBLIC_URL ||
|
|
148
|
-
`http://localhost:${env.
|
|
150
|
+
`http://localhost:${env.BIAWS_UI_PORT || 4_400}`,
|
|
149
151
|
storage: Object.values(STORAGE_FIELDS).some((key) => env[key])
|
|
150
152
|
? "directories"
|
|
151
153
|
: "volumes",
|
|
@@ -176,6 +178,50 @@ export async function getInstance(context, filesystem, name) {
|
|
|
176
178
|
return instance;
|
|
177
179
|
}
|
|
178
180
|
|
|
181
|
+
function normalizeVersion(value, label) {
|
|
182
|
+
const version = String(value || "").trim();
|
|
183
|
+
if (
|
|
184
|
+
!/^\d+\.\d+\.\d+(?:-[0-9A-Za-z.-]+)?(?:\+[0-9A-Za-z.-]+)?$/u.test(version)
|
|
185
|
+
) {
|
|
186
|
+
throw new CliError(`${label} inválida: ${version || "ausente"}.`, {
|
|
187
|
+
code: "INVALID_RELEASE_VERSION",
|
|
188
|
+
});
|
|
189
|
+
}
|
|
190
|
+
return version;
|
|
191
|
+
}
|
|
192
|
+
|
|
193
|
+
export async function getInstanceUpdateStatus(instance, context, filesystem) {
|
|
194
|
+
const manifestPath = path.join(
|
|
195
|
+
context.repositoryRoot,
|
|
196
|
+
"biaws-cli",
|
|
197
|
+
"package.json",
|
|
198
|
+
);
|
|
199
|
+
let manifest;
|
|
200
|
+
try {
|
|
201
|
+
manifest = JSON.parse(await filesystem.readFile(manifestPath, "utf8"));
|
|
202
|
+
} catch (error) {
|
|
203
|
+
throw new CliError(
|
|
204
|
+
`Não foi possível ler a versão da release em ${manifestPath}.`,
|
|
205
|
+
{
|
|
206
|
+
code: "RELEASE_VERSION_READ_FAILED",
|
|
207
|
+
cause: error,
|
|
208
|
+
},
|
|
209
|
+
);
|
|
210
|
+
}
|
|
211
|
+
const newVersion = normalizeVersion(manifest.version, "Versão da release");
|
|
212
|
+
const installed = String(instance.env.BIAWS_VERSION || "").trim();
|
|
213
|
+
const currentVersion =
|
|
214
|
+
installed && installed !== UNKNOWN_VERSION
|
|
215
|
+
? normalizeVersion(installed, "Versão instalada")
|
|
216
|
+
: UNKNOWN_VERSION;
|
|
217
|
+
return Object.freeze({
|
|
218
|
+
instance: instance.name,
|
|
219
|
+
currentVersion,
|
|
220
|
+
newVersion,
|
|
221
|
+
updateRequired: currentVersion !== newVersion,
|
|
222
|
+
});
|
|
223
|
+
}
|
|
224
|
+
|
|
179
225
|
function resolveStorage(values, existing) {
|
|
180
226
|
if (values.storage === "volumes") {
|
|
181
227
|
if (Object.keys(STORAGE_FIELDS).some((field) => values[field])) {
|
|
@@ -396,6 +442,8 @@ export function composeArguments(instance, context, operation) {
|
|
|
396
442
|
`biaws-${instance.name}`,
|
|
397
443
|
];
|
|
398
444
|
if (operation === "start") return [...base, "up", "-d", "--wait"];
|
|
445
|
+
if (operation === "update") return [...base, "up", "-d", "--build", "--wait"];
|
|
446
|
+
if (operation === "validate") return [...base, "config", "--quiet"];
|
|
399
447
|
if (operation === "stop") return [...base, "stop"];
|
|
400
448
|
return [...base, "ps", "--format", "json"];
|
|
401
449
|
}
|
|
@@ -405,15 +453,49 @@ export async function operateInstance(
|
|
|
405
453
|
context,
|
|
406
454
|
processRunner,
|
|
407
455
|
operation,
|
|
456
|
+
options = {},
|
|
408
457
|
) {
|
|
409
458
|
const result = await processRunner.run(
|
|
410
459
|
"docker",
|
|
411
460
|
composeArguments(instance, context, operation),
|
|
412
|
-
{
|
|
461
|
+
{
|
|
462
|
+
cwd: context.repositoryRoot,
|
|
463
|
+
env: options.environment,
|
|
464
|
+
silent: true,
|
|
465
|
+
},
|
|
413
466
|
);
|
|
414
467
|
return { instance: instance.name, operation, output: result.stdout.trim() };
|
|
415
468
|
}
|
|
416
469
|
|
|
470
|
+
export async function validateInstanceUpdate(instance, context, processRunner) {
|
|
471
|
+
return operateInstance(instance, context, processRunner, "validate");
|
|
472
|
+
}
|
|
473
|
+
|
|
474
|
+
export async function updateInstance(
|
|
475
|
+
instance,
|
|
476
|
+
context,
|
|
477
|
+
filesystem,
|
|
478
|
+
processRunner,
|
|
479
|
+
version,
|
|
480
|
+
environment = {},
|
|
481
|
+
) {
|
|
482
|
+
const normalizedVersion = normalizeVersion(version, "Nova versão");
|
|
483
|
+
const result = await operateInstance(
|
|
484
|
+
instance,
|
|
485
|
+
context,
|
|
486
|
+
processRunner,
|
|
487
|
+
"update",
|
|
488
|
+
{ environment: { ...environment, BIAWS_VERSION: normalizedVersion } },
|
|
489
|
+
);
|
|
490
|
+
const envContents = await filesystem.readFile(instance.envFile, "utf8");
|
|
491
|
+
const versionLine = `BIAWS_VERSION=${normalizedVersion}`;
|
|
492
|
+
const nextEnv = /^BIAWS_VERSION=.*$/mu.test(envContents)
|
|
493
|
+
? envContents.replace(/^BIAWS_VERSION=.*$/gmu, versionLine)
|
|
494
|
+
: `${envContents.replace(/\s*$/u, "")}\n${versionLine}\n`;
|
|
495
|
+
await filesystem.writeFile(instance.envFile, nextEnv, { mode: 0o600 });
|
|
496
|
+
return result;
|
|
497
|
+
}
|
|
498
|
+
|
|
417
499
|
export function validateArchivePath(value) {
|
|
418
500
|
const archive = path.resolve(String(value || ""));
|
|
419
501
|
if (!value || archive === path.parse(archive).root) {
|