biaws 0.1.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/LICENSE +201 -0
- package/README.md +229 -0
- package/bin/biaws.js +3 -0
- package/docs/command-taxonomy.md +36 -0
- package/docs/instance-lifecycle.md +82 -0
- package/docs/releasing.md +105 -0
- package/package.json +63 -0
- package/shared/skillPackage.js +100 -0
- package/src/apiClient.js +89 -0
- package/src/baseCommands.js +122 -0
- package/src/commands/agent/configure.js +3 -0
- package/src/commands/agent/doctor.js +3 -0
- package/src/commands/agent.js +400 -0
- package/src/commands/api.js +17 -0
- package/src/commands/applications/get.js +17 -0
- package/src/commands/applications/list.js +32 -0
- package/src/commands/applications.js +7 -0
- package/src/commands/configure/claude.js +25 -0
- package/src/commands/configure/codex.js +25 -0
- package/src/commands/configure/doctor.js +49 -0
- package/src/commands/configure/skills.js +74 -0
- package/src/commands/configure.js +19 -0
- package/src/commands/demands/complete-task.js +6 -0
- package/src/commands/demands/get.js +16 -0
- package/src/commands/demands/list.js +48 -0
- package/src/commands/demands/task-status.js +49 -0
- package/src/commands/demands/tasks.js +44 -0
- package/src/commands/demands.js +7 -0
- package/src/commands/instance/backup.js +74 -0
- package/src/commands/instance/list.js +39 -0
- package/src/commands/instance/remove.js +72 -0
- package/src/commands/instance/restore.js +96 -0
- package/src/commands/instance/setup.js +349 -0
- package/src/commands/instance/show.js +31 -0
- package/src/commands/instance/start.js +32 -0
- package/src/commands/instance/status.js +42 -0
- package/src/commands/instance/stop.js +30 -0
- package/src/commands/instance.js +18 -0
- package/src/commands/issues/get.js +16 -0
- package/src/commands/issues/list.js +51 -0
- package/src/commands/issues/transition.js +33 -0
- package/src/commands/issues.js +7 -0
- package/src/commands/monitoring/describe.js +13 -0
- package/src/commands/monitoring/signal.js +25 -0
- package/src/commands/monitoring/signals.js +17 -0
- package/src/commands/monitoring/validate.js +14 -0
- package/src/commands/monitoring.js +163 -0
- package/src/commands/skills/install-all.js +13 -0
- package/src/commands/skills/install.js +16 -0
- package/src/commands/skills/list.js +5 -0
- package/src/commands/skills/publish-all.js +14 -0
- package/src/commands/skills/publish.js +11 -0
- package/src/commands/skills/status.js +7 -0
- package/src/commands/skills/update.js +12 -0
- package/src/commands/skills.js +303 -0
- package/src/commands/workspaces/get.js +15 -0
- package/src/commands/workspaces/list.js +20 -0
- package/src/commands/workspaces.js +7 -0
- package/src/compatibilityCommands.js +107 -0
- package/src/configure/command.js +50 -0
- package/src/core/context.js +162 -0
- package/src/core/errors.js +25 -0
- package/src/core/processRunner.js +119 -0
- package/src/core/prompts.js +138 -0
- package/src/core/redaction.js +29 -0
- package/src/core/terminal.js +72 -0
- package/src/core/wizard.js +279 -0
- package/src/domain/readCommand.js +65 -0
- package/src/domain/readService.js +126 -0
- package/src/domain/writeCommand.js +91 -0
- package/src/domain/writeService.js +122 -0
- package/src/index.js +5 -0
- package/src/instance/command.js +37 -0
- package/src/instance/service.js +515 -0
- package/src/localSkills.js +143 -0
- package/src/skillPackage.js +4 -0
package/package.json
ADDED
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "biaws",
|
|
3
|
+
"version": "0.1.0",
|
|
4
|
+
"description": "CLI do Bondia Workspaces para configuração do ambiente local",
|
|
5
|
+
"license": "Apache-2.0",
|
|
6
|
+
"repository": {
|
|
7
|
+
"type": "git",
|
|
8
|
+
"url": "git+https://github.com/fbondia/biaws.git",
|
|
9
|
+
"directory": "biaws-cli"
|
|
10
|
+
},
|
|
11
|
+
"homepage": "https://github.com/fbondia/biaws#readme",
|
|
12
|
+
"bugs": {
|
|
13
|
+
"url": "https://github.com/fbondia/biaws/issues"
|
|
14
|
+
},
|
|
15
|
+
"keywords": [
|
|
16
|
+
"biaws",
|
|
17
|
+
"bondia-workspaces",
|
|
18
|
+
"cli",
|
|
19
|
+
"oclif"
|
|
20
|
+
],
|
|
21
|
+
"type": "module",
|
|
22
|
+
"engines": {
|
|
23
|
+
"node": ">=20.19.0"
|
|
24
|
+
},
|
|
25
|
+
"bin": {
|
|
26
|
+
"biaws": "bin/biaws.js"
|
|
27
|
+
},
|
|
28
|
+
"files": [
|
|
29
|
+
"bin",
|
|
30
|
+
"docs",
|
|
31
|
+
"LICENSE",
|
|
32
|
+
"shared",
|
|
33
|
+
"src",
|
|
34
|
+
"README.md"
|
|
35
|
+
],
|
|
36
|
+
"publishConfig": {
|
|
37
|
+
"access": "public"
|
|
38
|
+
},
|
|
39
|
+
"oclif": {
|
|
40
|
+
"bin": "biaws",
|
|
41
|
+
"dirname": "biaws",
|
|
42
|
+
"commands": "./src/commands",
|
|
43
|
+
"topicSeparator": " "
|
|
44
|
+
},
|
|
45
|
+
"scripts": {
|
|
46
|
+
"start": "node bin/biaws.js",
|
|
47
|
+
"format": "prettier --config ../prettier/.prettierrc --ignore-path ../.prettierignore --write .",
|
|
48
|
+
"format:check": "prettier --config ../prettier/.prettierrc --ignore-path ../.prettierignore --check .",
|
|
49
|
+
"check": "node scripts/check-syntax.mjs",
|
|
50
|
+
"test": "node --test",
|
|
51
|
+
"package:verify": "node scripts/verify-package.mjs",
|
|
52
|
+
"release:check": "npm run format:check && npm run check && npm test && npm run package:verify",
|
|
53
|
+
"publish:npm": "../scripts/publish-biaws-cli.sh",
|
|
54
|
+
"prepack": "npm run package:verify"
|
|
55
|
+
},
|
|
56
|
+
"devDependencies": {
|
|
57
|
+
"prettier": "^3.7.4"
|
|
58
|
+
},
|
|
59
|
+
"dependencies": {
|
|
60
|
+
"@inquirer/prompts": "^8.5.2",
|
|
61
|
+
"@oclif/core": "^4.13.5"
|
|
62
|
+
}
|
|
63
|
+
}
|
|
@@ -0,0 +1,100 @@
|
|
|
1
|
+
import crypto from "node:crypto";
|
|
2
|
+
import { lstat, readdir, readFile } from "node:fs/promises";
|
|
3
|
+
import path from "node:path";
|
|
4
|
+
|
|
5
|
+
const MAX_FILES = 200;
|
|
6
|
+
const MAX_FILE_BYTES = 512 * 1024;
|
|
7
|
+
const MAX_PACKAGE_BYTES = 2 * 1024 * 1024;
|
|
8
|
+
|
|
9
|
+
function normalizeRelativePath(value) {
|
|
10
|
+
return value.split(path.sep).join("/");
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
async function collectFiles(root, current = root, result = []) {
|
|
14
|
+
const entries = await readdir(current, { withFileTypes: true });
|
|
15
|
+
entries.sort((a, b) => a.name.localeCompare(b.name));
|
|
16
|
+
for (const entry of entries) {
|
|
17
|
+
const absolutePath = path.join(current, entry.name);
|
|
18
|
+
const relativePath = normalizeRelativePath(
|
|
19
|
+
path.relative(root, absolutePath),
|
|
20
|
+
);
|
|
21
|
+
if (entry.isSymbolicLink())
|
|
22
|
+
throw new Error(`Links simbólicos não são permitidos: ${relativePath}`);
|
|
23
|
+
if (entry.isDirectory()) await collectFiles(root, absolutePath, result);
|
|
24
|
+
else if (entry.isFile()) result.push({ absolutePath, path: relativePath });
|
|
25
|
+
}
|
|
26
|
+
return result;
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
function extractFrontmatter(markdown) {
|
|
30
|
+
const match = /^---\r?\n([\s\S]*?)\r?\n---/u.exec(markdown);
|
|
31
|
+
if (!match) return {};
|
|
32
|
+
const values = {};
|
|
33
|
+
for (const line of match[1].split(/\r?\n/u)) {
|
|
34
|
+
const separator = line.indexOf(":");
|
|
35
|
+
if (separator === -1) continue;
|
|
36
|
+
const key = line.slice(0, separator).trim();
|
|
37
|
+
const value = line.slice(separator + 1).trim();
|
|
38
|
+
if (key && value) values[key] = value;
|
|
39
|
+
}
|
|
40
|
+
return values;
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
export async function buildSkillPayload(directory, options = {}) {
|
|
44
|
+
const root = path.resolve(directory);
|
|
45
|
+
const stats = await lstat(root);
|
|
46
|
+
if (!stats.isDirectory())
|
|
47
|
+
throw new Error(`Diretório de skill inválido: ${root}`);
|
|
48
|
+
const entries = await collectFiles(root);
|
|
49
|
+
if (!entries.some((entry) => entry.path === "SKILL.md")) {
|
|
50
|
+
throw new Error(`SKILL.md não encontrado em ${root}`);
|
|
51
|
+
}
|
|
52
|
+
if (entries.length > MAX_FILES)
|
|
53
|
+
throw new Error(`A skill excede o limite de ${MAX_FILES} arquivos`);
|
|
54
|
+
|
|
55
|
+
let totalBytes = 0;
|
|
56
|
+
const files = [];
|
|
57
|
+
for (const entry of entries) {
|
|
58
|
+
const content = await readFile(entry.absolutePath);
|
|
59
|
+
if (content.length > MAX_FILE_BYTES)
|
|
60
|
+
throw new Error(`Arquivo excede ${MAX_FILE_BYTES} bytes: ${entry.path}`);
|
|
61
|
+
totalBytes += content.length;
|
|
62
|
+
if (totalBytes > MAX_PACKAGE_BYTES)
|
|
63
|
+
throw new Error(`A skill excede o limite de ${MAX_PACKAGE_BYTES} bytes`);
|
|
64
|
+
files.push({ path: entry.path, contentBase64: content.toString("base64") });
|
|
65
|
+
}
|
|
66
|
+
const skillMarkdown = await readFile(path.join(root, "SKILL.md"), "utf8");
|
|
67
|
+
const frontmatter = extractFrontmatter(skillMarkdown);
|
|
68
|
+
const skillId = String(
|
|
69
|
+
options.id || frontmatter.name || path.basename(root),
|
|
70
|
+
).trim();
|
|
71
|
+
const description = String(
|
|
72
|
+
options.description || frontmatter.description || "",
|
|
73
|
+
).trim();
|
|
74
|
+
if (!description) {
|
|
75
|
+
throw new Error(
|
|
76
|
+
"Descrição ausente; informe --description ou adicione description ao SKILL.md",
|
|
77
|
+
);
|
|
78
|
+
}
|
|
79
|
+
return {
|
|
80
|
+
skillId,
|
|
81
|
+
name: String(options.name || skillId),
|
|
82
|
+
description,
|
|
83
|
+
version: String(options.version || ""),
|
|
84
|
+
changelog: String(options.changelog || ""),
|
|
85
|
+
files,
|
|
86
|
+
};
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
export function checksumPackageFiles(files) {
|
|
90
|
+
const hash = crypto.createHash("sha256");
|
|
91
|
+
for (const file of [...files].sort((a, b) => a.path.localeCompare(b.path))) {
|
|
92
|
+
const content = Buffer.from(file.contentBase64, "base64");
|
|
93
|
+
const fileHash = crypto.createHash("sha256").update(content).digest("hex");
|
|
94
|
+
hash.update(file.path);
|
|
95
|
+
hash.update("\0");
|
|
96
|
+
hash.update(fileHash);
|
|
97
|
+
hash.update("\0");
|
|
98
|
+
}
|
|
99
|
+
return hash.digest("hex");
|
|
100
|
+
}
|
package/src/apiClient.js
ADDED
|
@@ -0,0 +1,89 @@
|
|
|
1
|
+
function trimTrailingSlash(value) {
|
|
2
|
+
return String(value || "").replace(/\/+$/u, "");
|
|
3
|
+
}
|
|
4
|
+
|
|
5
|
+
export function createApiClient(baseUrl, apiKey, workspaceId = "") {
|
|
6
|
+
const apiRoot = `${trimTrailingSlash(baseUrl)}/api`;
|
|
7
|
+
const root = `${apiRoot}/skills`;
|
|
8
|
+
|
|
9
|
+
async function request(path = "", options = {}, requestRoot = root) {
|
|
10
|
+
const response = await fetch(`${requestRoot}${path}`, {
|
|
11
|
+
...options,
|
|
12
|
+
headers: {
|
|
13
|
+
Accept: "application/json",
|
|
14
|
+
...(apiKey ? { Authorization: `Bearer ${apiKey}` } : {}),
|
|
15
|
+
...(workspaceId ? { "X-Biaws-Workspace-Id": workspaceId } : {}),
|
|
16
|
+
...(options.body ? { "Content-Type": "application/json" } : {}),
|
|
17
|
+
...options.headers,
|
|
18
|
+
},
|
|
19
|
+
});
|
|
20
|
+
const data = await response.json().catch(() => null);
|
|
21
|
+
if (!response.ok) {
|
|
22
|
+
const error = new Error(
|
|
23
|
+
data?.error?.message || `HTTP ${response.status}`,
|
|
24
|
+
);
|
|
25
|
+
error.statusCode = response.status;
|
|
26
|
+
error.code =
|
|
27
|
+
data?.error?.code ||
|
|
28
|
+
(response.status === 401
|
|
29
|
+
? "UNAUTHENTICATED"
|
|
30
|
+
: response.status === 403
|
|
31
|
+
? "FORBIDDEN"
|
|
32
|
+
: response.status === 404
|
|
33
|
+
? "NOT_FOUND"
|
|
34
|
+
: response.status === 409
|
|
35
|
+
? "CONFLICT"
|
|
36
|
+
: "ISSUE_API_ERROR");
|
|
37
|
+
throw error;
|
|
38
|
+
}
|
|
39
|
+
return data;
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
return {
|
|
43
|
+
request: (path, options = {}) => request(path, options, apiRoot),
|
|
44
|
+
identity: () => request("/auth/me", {}, apiRoot),
|
|
45
|
+
list: (options = {}) =>
|
|
46
|
+
request(options.includeDeprecated ? "?includeDeprecated=true" : ""),
|
|
47
|
+
get: (skillId, version) =>
|
|
48
|
+
request(
|
|
49
|
+
`/${encodeURIComponent(skillId)}${version ? `?version=${encodeURIComponent(version)}` : ""}`,
|
|
50
|
+
),
|
|
51
|
+
download: (skillId, version) =>
|
|
52
|
+
request(
|
|
53
|
+
`/${encodeURIComponent(skillId)}/${encodeURIComponent(version)}/download`,
|
|
54
|
+
),
|
|
55
|
+
publish: (payload) =>
|
|
56
|
+
request("", { method: "POST", body: JSON.stringify(payload) }),
|
|
57
|
+
monitoring: {
|
|
58
|
+
describeTemplate: (templateId, version) =>
|
|
59
|
+
request(
|
|
60
|
+
`/templates/${encodeURIComponent(templateId)}/versions/${encodeURIComponent(version)}/contract`,
|
|
61
|
+
{},
|
|
62
|
+
`${apiRoot}/monitoring`,
|
|
63
|
+
),
|
|
64
|
+
validateTemplate: (templateId, version, sample) =>
|
|
65
|
+
request(
|
|
66
|
+
`/templates/${encodeURIComponent(templateId)}/versions/${encodeURIComponent(version)}/validate`,
|
|
67
|
+
{ method: "POST", body: JSON.stringify({ sample }) },
|
|
68
|
+
`${apiRoot}/monitoring`,
|
|
69
|
+
),
|
|
70
|
+
signal: (runtimeReference, payload) =>
|
|
71
|
+
request(
|
|
72
|
+
`/runtimes/${encodeURIComponent(runtimeReference)}/signals`,
|
|
73
|
+
{ method: "POST", body: JSON.stringify(payload) },
|
|
74
|
+
`${apiRoot}/monitoring`,
|
|
75
|
+
),
|
|
76
|
+
listSignals: (runtimeReference, options = {}) => {
|
|
77
|
+
const parameters = new URLSearchParams();
|
|
78
|
+
if (options.page) parameters.set("page", options.page);
|
|
79
|
+
if (options.limit) parameters.set("limit", options.limit);
|
|
80
|
+
const query = parameters.size ? `?${parameters}` : "";
|
|
81
|
+
return request(
|
|
82
|
+
`/runtimes/${encodeURIComponent(runtimeReference)}/signals${query}`,
|
|
83
|
+
{},
|
|
84
|
+
`${apiRoot}/monitoring`,
|
|
85
|
+
);
|
|
86
|
+
},
|
|
87
|
+
},
|
|
88
|
+
};
|
|
89
|
+
}
|
|
@@ -0,0 +1,122 @@
|
|
|
1
|
+
import {
|
|
2
|
+
access,
|
|
3
|
+
chmod,
|
|
4
|
+
mkdtemp,
|
|
5
|
+
mkdir,
|
|
6
|
+
readFile,
|
|
7
|
+
readdir,
|
|
8
|
+
stat,
|
|
9
|
+
rm,
|
|
10
|
+
writeFile,
|
|
11
|
+
} from "node:fs/promises";
|
|
12
|
+
import path from "node:path";
|
|
13
|
+
import { fileURLToPath } from "node:url";
|
|
14
|
+
|
|
15
|
+
import { Command } from "@oclif/core";
|
|
16
|
+
|
|
17
|
+
import { createApiClient } from "./apiClient.js";
|
|
18
|
+
import {
|
|
19
|
+
resolveAuthenticatedContext,
|
|
20
|
+
resolveCommandContext,
|
|
21
|
+
} from "./core/context.js";
|
|
22
|
+
import { ProcessRunner } from "./core/processRunner.js";
|
|
23
|
+
import { createPromptAdapter } from "./core/prompts.js";
|
|
24
|
+
import {
|
|
25
|
+
CliLogger,
|
|
26
|
+
CliOutput,
|
|
27
|
+
createTerminalAdapter,
|
|
28
|
+
} from "./core/terminal.js";
|
|
29
|
+
|
|
30
|
+
export const TOOL_DIRECTORY = path.resolve(
|
|
31
|
+
path.dirname(fileURLToPath(import.meta.url)),
|
|
32
|
+
"..",
|
|
33
|
+
);
|
|
34
|
+
|
|
35
|
+
export function createCommandAdapters(overrides = {}) {
|
|
36
|
+
const environment = overrides.environment || { ...process.env };
|
|
37
|
+
const filesystem = overrides.filesystem || {
|
|
38
|
+
access,
|
|
39
|
+
chmod,
|
|
40
|
+
mkdtemp,
|
|
41
|
+
mkdir,
|
|
42
|
+
readFile,
|
|
43
|
+
readdir,
|
|
44
|
+
stat,
|
|
45
|
+
rm,
|
|
46
|
+
writeFile,
|
|
47
|
+
};
|
|
48
|
+
const terminal = overrides.terminal || createTerminalAdapter({ environment });
|
|
49
|
+
const processRunner =
|
|
50
|
+
overrides.processRunner ||
|
|
51
|
+
new ProcessRunner({
|
|
52
|
+
signalSource: process,
|
|
53
|
+
stdout: terminal.stdout,
|
|
54
|
+
stderr: terminal.stderr,
|
|
55
|
+
});
|
|
56
|
+
const prompts =
|
|
57
|
+
overrides.prompts || createPromptAdapter(terminal, overrides.promptOptions);
|
|
58
|
+
return Object.freeze({
|
|
59
|
+
apiFactory: overrides.apiFactory || createApiClient,
|
|
60
|
+
cwd: overrides.cwd || (() => process.cwd()),
|
|
61
|
+
environment,
|
|
62
|
+
filesystem,
|
|
63
|
+
processRunner,
|
|
64
|
+
prompts,
|
|
65
|
+
terminal,
|
|
66
|
+
});
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
export class BaseCommand extends Command {
|
|
70
|
+
constructor(argv, config, adapterOverrides = {}) {
|
|
71
|
+
super(argv, config);
|
|
72
|
+
this.adapters = createCommandAdapters(adapterOverrides);
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
output(options = {}) {
|
|
76
|
+
return new CliOutput(this.adapters.terminal, options);
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
logger(options = {}) {
|
|
80
|
+
return new CliLogger(this.adapters.terminal, options);
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
contextOptions(input = {}) {
|
|
84
|
+
return {
|
|
85
|
+
cwd: this.adapters.cwd(),
|
|
86
|
+
environment: this.adapters.environment,
|
|
87
|
+
filesystem: this.adapters.filesystem,
|
|
88
|
+
input,
|
|
89
|
+
terminal: this.adapters.terminal,
|
|
90
|
+
toolDirectory: TOOL_DIRECTORY,
|
|
91
|
+
};
|
|
92
|
+
}
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
export class LocalInstanceCommand extends BaseCommand {
|
|
96
|
+
localContext(input = {}) {
|
|
97
|
+
return resolveCommandContext(this.contextOptions(input));
|
|
98
|
+
}
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
export class AuthenticatedApiCommand extends BaseCommand {
|
|
102
|
+
async authenticatedContext(input = {}, options = {}) {
|
|
103
|
+
const context = await resolveAuthenticatedContext({
|
|
104
|
+
...this.contextOptions(input),
|
|
105
|
+
requireWorkspace: Boolean(options.requireWorkspace),
|
|
106
|
+
});
|
|
107
|
+
return Object.freeze({
|
|
108
|
+
...context,
|
|
109
|
+
api: this.adapters.apiFactory(
|
|
110
|
+
context.apiUrl,
|
|
111
|
+
context.apiKey,
|
|
112
|
+
context.workspaceId,
|
|
113
|
+
),
|
|
114
|
+
});
|
|
115
|
+
}
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
export class ProjectCommand extends AuthenticatedApiCommand {
|
|
119
|
+
async projectContext(input = {}, options = {}) {
|
|
120
|
+
return this.authenticatedContext(input, options);
|
|
121
|
+
}
|
|
122
|
+
}
|