@finebi/finebi-cli 1.5.0 → 1.6.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 +10 -0
- package/package.json +4 -3
- package/skills/finebi-cli-agent/SKILL.md +8 -4
- package/skills/finebi-cli-agent/references/command-workflows.md +7 -3
- package/skills/finebi-cli-agent/references/dashboard-data-result.md +191 -191
- package/skills/finebi-cli-agent/references/dashboard-dsl-api.md +8 -1018
- package/skills/finebi-cli-agent/references/dashboard-dsl-core.md +194 -0
- package/skills/finebi-cli-agent/references/dashboard-dsl-examples.md +378 -0
- package/skills/finebi-cli-agent/references/dashboard-dsl-items.md +456 -0
- package/skills/finebi-cli-agent/references/mapping-rules.md +14 -14
- package/skills/finebi-cli-agent/references/scenario-workflows.md +1 -1
- package/skills/finebi-cli-agent/references/web-app.md +13 -0
- package/skills/finebi-cli-agent/references/widget-dsl-api-examples.md +3 -4
- package/skills/finebi-cli-agent/references/widget-dsl-api.md +10 -1374
- package/skills/finebi-cli-agent/references/widget-dsl-contexts.md +142 -0
- package/skills/finebi-cli-agent/references/widget-dsl-core.md +374 -0
- package/skills/finebi-cli-agent/references/widget-dsl-encodings.md +457 -0
- package/skills/finebi-cli-agent/references/widget-dsl-styles.md +413 -0
- package/skills/finebi-cli-agent/scripts/finebi-cli/package.json +1 -1
- package/skills/finebi-cli-agent/scripts/finebi-cli/src/app.js +189 -0
- package/skills/finebi-cli-agent/scripts/finebi-cli/src/config.js +27 -27
- package/skills/finebi-cli-agent/scripts/finebi-cli/src/help.js +78 -55
- package/skills/finebi-cli-agent/scripts/finebi-cli/src/index.js +5 -0
- package/skills/finebi-cli-agent/scripts/finebi-cli/src/install-skill.js +89 -89
- package/skills/finebi-cli-agent/scripts/finebi-cli/src/profiles.js +158 -158
- package/skills/finebi-cli-agent/scripts/finebi-cli/src/skill-files.js +94 -94
- package/skills/finebi-cli-agent/scripts/finebi-cli/templates/web-app/AGENTS.md +27 -0
- package/skills/finebi-cli-agent/scripts/finebi-cli/templates/web-app/README.md +30 -0
- package/skills/finebi-cli-agent/scripts/finebi-cli/templates/web-app/docs/auth-and-deploy.md +17 -0
- package/skills/finebi-cli-agent/scripts/finebi-cli/templates/web-app/docs/data-contract.md +21 -0
- package/skills/finebi-cli-agent/scripts/finebi-cli/templates/web-app/docs/design.md +13 -0
- package/skills/finebi-cli-agent/scripts/finebi-cli/templates/web-app/docs/filters.md +89 -0
- package/skills/finebi-cli-agent/scripts/finebi-cli/templates/web-app/index.html +2 -0
- package/skills/finebi-cli-agent/scripts/finebi-cli/templates/web-app/package-lock.json +2191 -0
- package/skills/finebi-cli-agent/scripts/finebi-cli/templates/web-app/package.json +15 -0
- package/skills/finebi-cli-agent/scripts/finebi-cli/templates/web-app/public/app-config.json +11 -0
- package/skills/finebi-cli-agent/scripts/finebi-cli/templates/web-app/scripts/dev-proxy.mjs +56 -0
- package/skills/finebi-cli-agent/scripts/finebi-cli/templates/web-app/src/App.tsx +31 -0
- package/skills/finebi-cli-agent/scripts/finebi-cli/templates/web-app/src/components/Chart.tsx +31 -0
- package/skills/finebi-cli-agent/scripts/finebi-cli/templates/web-app/src/components/DataTable.tsx +14 -0
- package/skills/finebi-cli-agent/scripts/finebi-cli/templates/web-app/src/components/FilterBar.tsx +42 -0
- package/skills/finebi-cli-agent/scripts/finebi-cli/templates/web-app/src/components/Widget.tsx +28 -0
- package/skills/finebi-cli-agent/scripts/finebi-cli/templates/web-app/src/components/calendar.ts +14 -0
- package/skills/finebi-cli-agent/scripts/finebi-cli/templates/web-app/src/finebi/client.ts +37 -0
- package/skills/finebi-cli-agent/scripts/finebi-cli/templates/web-app/src/finebi/date.d.ts +1 -0
- package/skills/finebi-cli-agent/scripts/finebi-cli/templates/web-app/src/finebi/date.js +5 -0
- package/skills/finebi-cli-agent/scripts/finebi-cli/templates/web-app/src/finebi/filters.d.ts +4 -0
- package/skills/finebi-cli-agent/scripts/finebi-cli/templates/web-app/src/finebi/filters.js +106 -0
- package/skills/finebi-cli-agent/scripts/finebi-cli/templates/web-app/src/finebi/normalize.ts +77 -0
- package/skills/finebi-cli-agent/scripts/finebi-cli/templates/web-app/src/finebi/types.ts +18 -0
- package/skills/finebi-cli-agent/scripts/finebi-cli/templates/web-app/src/main.tsx +5 -0
- package/skills/finebi-cli-agent/scripts/finebi-cli/templates/web-app/src/services/view-data.ts +18 -0
- package/skills/finebi-cli-agent/scripts/finebi-cli/templates/web-app/src/styles.css +8 -0
- package/skills/finebi-cli-agent/scripts/finebi-cli/templates/web-app/tests/calendar.test.mjs +25 -0
- package/skills/finebi-cli-agent/scripts/finebi-cli/templates/web-app/tests/proxy.test.mjs +84 -0
- package/skills/finebi-cli-agent/scripts/finebi-cli/templates/web-app/tests/runtime.test.mjs +136 -0
- package/skills/finebi-cli-agent/scripts/finebi-cli/templates/web-app/tsconfig.json +4 -0
- package/skills/finebi-cli-agent/scripts/finebi-cli/templates/web-app/vite.config.ts +13 -0
- package/skills/finebi-cli-agent/scripts/finebi-cli-runner.mjs +4 -4
- package/src/app.js +189 -0
- package/src/config.js +27 -27
- package/src/help.js +78 -55
- package/src/index.js +5 -0
- package/src/install-skill.js +89 -89
- package/src/profiles.js +158 -158
- package/src/skill-files.js +94 -94
- package/templates/web-app/AGENTS.md +27 -0
- package/templates/web-app/README.md +30 -0
- package/templates/web-app/docs/auth-and-deploy.md +17 -0
- package/templates/web-app/docs/data-contract.md +21 -0
- package/templates/web-app/docs/design.md +13 -0
- package/templates/web-app/docs/filters.md +89 -0
- package/templates/web-app/index.html +2 -0
- package/templates/web-app/package-lock.json +2191 -0
- package/templates/web-app/package.json +15 -0
- package/templates/web-app/public/app-config.json +11 -0
- package/templates/web-app/scripts/dev-proxy.mjs +56 -0
- package/templates/web-app/src/App.tsx +31 -0
- package/templates/web-app/src/components/Chart.tsx +31 -0
- package/templates/web-app/src/components/DataTable.tsx +14 -0
- package/templates/web-app/src/components/FilterBar.tsx +42 -0
- package/templates/web-app/src/components/Widget.tsx +28 -0
- package/templates/web-app/src/components/calendar.ts +14 -0
- package/templates/web-app/src/finebi/client.ts +37 -0
- package/templates/web-app/src/finebi/date.d.ts +1 -0
- package/templates/web-app/src/finebi/date.js +5 -0
- package/templates/web-app/src/finebi/filters.d.ts +4 -0
- package/templates/web-app/src/finebi/filters.js +106 -0
- package/templates/web-app/src/finebi/normalize.ts +77 -0
- package/templates/web-app/src/finebi/types.ts +18 -0
- package/templates/web-app/src/main.tsx +5 -0
- package/templates/web-app/src/services/view-data.ts +18 -0
- package/templates/web-app/src/styles.css +8 -0
- package/templates/web-app/tests/calendar.test.mjs +25 -0
- package/templates/web-app/tests/proxy.test.mjs +84 -0
- package/templates/web-app/tests/runtime.test.mjs +136 -0
- package/templates/web-app/tsconfig.json +4 -0
- package/templates/web-app/vite.config.ts +13 -0
package/src/help.js
CHANGED
|
@@ -1,12 +1,12 @@
|
|
|
1
1
|
import {brandCommandValue, CLI_COMMAND_NAME, CLI_DISPLAY_NAME, commandLine} from "./branding.js";
|
|
2
2
|
import {cliError} from "./errors.js";
|
|
3
|
-
import {failure, success} from "./output.js";
|
|
4
|
-
|
|
5
|
-
const GLOBAL_OPTIONS = [
|
|
6
|
-
{name: "--profile", type: "string", description: "Environment name. Overrides FINEBI_PROFILE and the saved default; ordinary commands do not create environments or switch the default."},
|
|
7
|
-
{name: "--base-url", type: "string", description: "Connection URL override. A different server never inherits saved credentials or capabilities."},
|
|
8
|
-
{name: "--token", type: "string", description: "Temporary session token override; not persisted."},
|
|
9
|
-
];
|
|
3
|
+
import {failure, success} from "./output.js";
|
|
4
|
+
|
|
5
|
+
const GLOBAL_OPTIONS = [
|
|
6
|
+
{name: "--profile", type: "string", description: "Environment name. Overrides FINEBI_PROFILE and the saved default; ordinary commands do not create environments or switch the default."},
|
|
7
|
+
{name: "--base-url", type: "string", description: "Connection URL override. A different server never inherits saved credentials or capabilities."},
|
|
8
|
+
{name: "--token", type: "string", description: "Temporary session token override; not persisted."},
|
|
9
|
+
];
|
|
10
10
|
|
|
11
11
|
export function renderTopLevelHelp(manifest) {
|
|
12
12
|
return success("help", {
|
|
@@ -14,8 +14,8 @@ export function renderTopLevelHelp(manifest) {
|
|
|
14
14
|
name: resource.name,
|
|
15
15
|
summary: resource.summary,
|
|
16
16
|
})),
|
|
17
|
-
dynamicCapabilitiesLoaded: Boolean(manifest),
|
|
18
|
-
globalOptions: GLOBAL_OPTIONS,
|
|
17
|
+
dynamicCapabilitiesLoaded: Boolean(manifest),
|
|
18
|
+
globalOptions: GLOBAL_OPTIONS,
|
|
19
19
|
}, { schemaVersion: manifest?.schemaVersion });
|
|
20
20
|
}
|
|
21
21
|
|
|
@@ -24,7 +24,7 @@ export function renderBuiltInHelp(resourceName) {
|
|
|
24
24
|
if (!resource) {
|
|
25
25
|
return capabilityNotFound(resourceName);
|
|
26
26
|
}
|
|
27
|
-
return success("help", brandCommandValue({...resource, globalOptions: GLOBAL_OPTIONS}));
|
|
27
|
+
return success("help", brandCommandValue({...resource, globalOptions: GLOBAL_OPTIONS}));
|
|
28
28
|
}
|
|
29
29
|
|
|
30
30
|
export function renderBuiltInCommandHelp(resourceName, commandName) {
|
|
@@ -38,8 +38,8 @@ export function renderBuiltInCommandHelp(resourceName, commandName) {
|
|
|
38
38
|
command: command.name,
|
|
39
39
|
summary: command.summary,
|
|
40
40
|
usage: command.usage,
|
|
41
|
-
options: command.options || [],
|
|
42
|
-
globalOptions: GLOBAL_OPTIONS,
|
|
41
|
+
options: command.options || [],
|
|
42
|
+
globalOptions: GLOBAL_OPTIONS,
|
|
43
43
|
details: command.details || [],
|
|
44
44
|
examples: command.examples || [],
|
|
45
45
|
}));
|
|
@@ -52,8 +52,8 @@ export function renderResourceHelp(manifest, resourceName) {
|
|
|
52
52
|
}
|
|
53
53
|
return success("help", {
|
|
54
54
|
resource: resource.name,
|
|
55
|
-
summary: resource.summary,
|
|
56
|
-
globalOptions: GLOBAL_OPTIONS,
|
|
55
|
+
summary: resource.summary,
|
|
56
|
+
globalOptions: GLOBAL_OPTIONS,
|
|
57
57
|
commands: (resource.commands || []).map((command) => ({
|
|
58
58
|
name: command.name,
|
|
59
59
|
summary: command.summary,
|
|
@@ -72,8 +72,8 @@ export function renderCommandHelp(manifest, resourceName, commandName) {
|
|
|
72
72
|
command: command.name,
|
|
73
73
|
summary: command.summary,
|
|
74
74
|
usage: usageFor(resource.name, command),
|
|
75
|
-
options: commandOptionsFor(command),
|
|
76
|
-
globalOptions: GLOBAL_OPTIONS,
|
|
75
|
+
options: commandOptionsFor(command),
|
|
76
|
+
globalOptions: GLOBAL_OPTIONS,
|
|
77
77
|
display: command.display,
|
|
78
78
|
requestSchema: command.requestSchema,
|
|
79
79
|
responseSchema: command.responseSchema,
|
|
@@ -99,6 +99,29 @@ function findResource(manifest, resourceName) {
|
|
|
99
99
|
}
|
|
100
100
|
|
|
101
101
|
const BUILT_IN_RESOURCES = {
|
|
102
|
+
app: {
|
|
103
|
+
resource: "app",
|
|
104
|
+
summary: "Create and inspect local FineBI dynamic web app projects without a server connection.",
|
|
105
|
+
commands: [
|
|
106
|
+
{
|
|
107
|
+
name: "create",
|
|
108
|
+
summary: "Copy the bundled web app template into a new project directory.",
|
|
109
|
+
usage: "finebi-cli app create --name <name> --path <parent>",
|
|
110
|
+
options: [
|
|
111
|
+
{name: "--name", type: "string", description: "Simple lowercase npm project name."},
|
|
112
|
+
{name: "--path", type: "string", description: "Existing parent directory; relative paths resolve from the current directory."},
|
|
113
|
+
],
|
|
114
|
+
details: ["Creates a local project only; it does not connect to FineBI or run install/build scripts."],
|
|
115
|
+
},
|
|
116
|
+
{
|
|
117
|
+
name: "inspect",
|
|
118
|
+
summary: "Check a local web app project configuration without remote validation.",
|
|
119
|
+
usage: "finebi-cli app inspect --path <project>",
|
|
120
|
+
options: [{name: "--path", type: "string", description: "Existing project directory; relative paths resolve from the current directory."}],
|
|
121
|
+
details: ["Checks app-config.json structure and widget bindings locally. Remote dashboard and permission checks are not performed."],
|
|
122
|
+
},
|
|
123
|
+
],
|
|
124
|
+
},
|
|
102
125
|
"install-skill": {
|
|
103
126
|
command: "install-skill",
|
|
104
127
|
resource: "install-skill",
|
|
@@ -152,7 +175,7 @@ const BUILT_IN_RESOURCES = {
|
|
|
152
175
|
},
|
|
153
176
|
{
|
|
154
177
|
name: "clear",
|
|
155
|
-
summary: "Remove the selected environment's local configuration, credentials and capability cache; does not revoke server keys.",
|
|
178
|
+
summary: "Remove the selected environment's local configuration, credentials and capability cache; does not revoke server keys.",
|
|
156
179
|
usage: "finebi-cli config clear",
|
|
157
180
|
},
|
|
158
181
|
],
|
|
@@ -164,18 +187,18 @@ const BUILT_IN_RESOURCES = {
|
|
|
164
187
|
{
|
|
165
188
|
name: "login",
|
|
166
189
|
summary: "Login with FineBI credentials or browser authorization and persist an Access Key.",
|
|
167
|
-
usage: "finebi-cli auth login [--profile <name>] [--base-url <url>] [--browser] [--username <username>] [--from-env] [--password-stdin] [--no-use]",
|
|
168
|
-
options: [
|
|
169
|
-
{ name: "--no-use", type: "boolean", description: "Save successful login without changing the default environment." },
|
|
190
|
+
usage: "finebi-cli auth login [--profile <name>] [--base-url <url>] [--browser] [--username <username>] [--from-env] [--password-stdin] [--no-use]",
|
|
191
|
+
options: [
|
|
192
|
+
{ name: "--no-use", type: "boolean", description: "Save successful login without changing the default environment." },
|
|
170
193
|
{ name: "--username", type: "string", description: "FineBI username." },
|
|
171
194
|
{ name: "--from-env", type: "boolean", description: "Read base URL, username, and password from environment variables." },
|
|
172
195
|
{ name: "--password-stdin", type: "boolean", description: "Read password from standard input." },
|
|
173
196
|
{ name: "--browser", type: "boolean", description: "Authorize this CLI from a signed-in FineBI browser without sending a password." },
|
|
174
197
|
],
|
|
175
|
-
details: [
|
|
176
|
-
"Login creates the named environment if missing and selects it after successful authentication, unless --no-use is supplied.",
|
|
177
|
-
"A new environment requires --base-url or FINEBI_BASE_URL; it never inherits another environment's address or identity.",
|
|
178
|
-
"Failed authentication preserves saved environments and the default selection.",
|
|
198
|
+
details: [
|
|
199
|
+
"Login creates the named environment if missing and selects it after successful authentication, unless --no-use is supplied.",
|
|
200
|
+
"A new environment requires --base-url or FINEBI_BASE_URL; it never inherits another environment's address or identity.",
|
|
201
|
+
"Failed authentication preserves saved environments and the default selection.",
|
|
179
202
|
"Environment variables for --from-env:",
|
|
180
203
|
" FINEBI_BASE_URL",
|
|
181
204
|
" FINEBI_USERNAME",
|
|
@@ -187,28 +210,28 @@ const BUILT_IN_RESOURCES = {
|
|
|
187
210
|
"finebi-cli auth login --username <username>",
|
|
188
211
|
"finebi-cli auth login --from-env",
|
|
189
212
|
"echo <password> | finebi-cli auth login --username <username> --password-stdin",
|
|
190
|
-
"finebi-cli auth login --browser",
|
|
191
|
-
"finebi-cli auth login --profile test --base-url https://test.example.com/webroot/decision --browser",
|
|
213
|
+
"finebi-cli auth login --browser",
|
|
214
|
+
"finebi-cli auth login --profile test --base-url https://test.example.com/webroot/decision --browser",
|
|
192
215
|
],
|
|
193
216
|
},
|
|
194
|
-
{
|
|
195
|
-
name: "list",
|
|
196
|
-
summary: "List saved environments and mark the default; local metadata only, no credential validation or secret output.",
|
|
197
|
-
usage: "finebi-cli auth list",
|
|
198
|
-
},
|
|
199
|
-
{
|
|
200
|
-
name: "use",
|
|
201
|
-
summary: "Select an existing environment as default without logging in.",
|
|
202
|
-
usage: "finebi-cli auth use <name>",
|
|
203
|
-
},
|
|
204
|
-
{
|
|
205
|
-
name: "remove",
|
|
206
|
-
summary: "Delete one local environment and its capability cache; does not revoke server keys.",
|
|
207
|
-
usage: "finebi-cli auth remove <name>",
|
|
208
|
-
details: ["Use auth logout --profile <name> first to revoke its Access Key.", "Removing the default environment does not select another environment."],
|
|
209
|
-
},
|
|
210
|
-
{
|
|
211
|
-
name: "info",
|
|
217
|
+
{
|
|
218
|
+
name: "list",
|
|
219
|
+
summary: "List saved environments and mark the default; local metadata only, no credential validation or secret output.",
|
|
220
|
+
usage: "finebi-cli auth list",
|
|
221
|
+
},
|
|
222
|
+
{
|
|
223
|
+
name: "use",
|
|
224
|
+
summary: "Select an existing environment as default without logging in.",
|
|
225
|
+
usage: "finebi-cli auth use <name>",
|
|
226
|
+
},
|
|
227
|
+
{
|
|
228
|
+
name: "remove",
|
|
229
|
+
summary: "Delete one local environment and its capability cache; does not revoke server keys.",
|
|
230
|
+
usage: "finebi-cli auth remove <name>",
|
|
231
|
+
details: ["Use auth logout --profile <name> first to revoke its Access Key.", "Removing the default environment does not select another environment."],
|
|
232
|
+
},
|
|
233
|
+
{
|
|
234
|
+
name: "info",
|
|
212
235
|
summary: "Show stored authentication metadata with secrets masked.",
|
|
213
236
|
usage: "finebi-cli auth info",
|
|
214
237
|
},
|
|
@@ -376,10 +399,10 @@ function renderTopLevelHelpText(data) {
|
|
|
376
399
|
"",
|
|
377
400
|
"Use --help or -h for command details.",
|
|
378
401
|
"Use --json with --help for structured output.",
|
|
379
|
-
"Output options: --select <paths> / --max-chars <n> / --save <path> for large responses.",
|
|
380
|
-
"Environment: --profile <name> > FINEBI_PROFILE > saved default. Global options may precede or follow the command.",
|
|
381
|
-
);
|
|
382
|
-
lines.push("", "Global Options:", ...formatOptionRows(data.globalOptions || []));
|
|
402
|
+
"Output options: --select <paths> / --max-chars <n> / --save <path> for large responses.",
|
|
403
|
+
"Environment: --profile <name> > FINEBI_PROFILE > saved default. Global options may precede or follow the command.",
|
|
404
|
+
);
|
|
405
|
+
lines.push("", "Global Options:", ...formatOptionRows(data.globalOptions || []));
|
|
383
406
|
return `${lines.join("\n")}\n`;
|
|
384
407
|
}
|
|
385
408
|
|
|
@@ -395,8 +418,8 @@ function renderResourceHelpText(data) {
|
|
|
395
418
|
"",
|
|
396
419
|
"Commands:",
|
|
397
420
|
];
|
|
398
|
-
lines.push(...formatRows(data.commands || [], "name", "summary"));
|
|
399
|
-
lines.push("", "Global Options:", ...formatOptionRows(data.globalOptions || []));
|
|
421
|
+
lines.push(...formatRows(data.commands || [], "name", "summary"));
|
|
422
|
+
lines.push("", "Global Options:", ...formatOptionRows(data.globalOptions || []));
|
|
400
423
|
lines.push("", `Use ${commandLine(data.resource, "<command>", "--help")} for command details.`);
|
|
401
424
|
return `${lines.join("\n")}\n`;
|
|
402
425
|
}
|
|
@@ -411,13 +434,13 @@ function renderCommandHelpText(data) {
|
|
|
411
434
|
"Usage:",
|
|
412
435
|
` ${data.usage}`,
|
|
413
436
|
];
|
|
414
|
-
if ((data.options || []).length > 0) {
|
|
437
|
+
if ((data.options || []).length > 0) {
|
|
415
438
|
lines.push("", "Options:");
|
|
416
439
|
lines.push(...formatOptionRows(data.options));
|
|
417
|
-
}
|
|
418
|
-
if ((data.globalOptions || []).length > 0) {
|
|
419
|
-
lines.push("", "Global Options:", ...formatOptionRows(data.globalOptions));
|
|
420
|
-
}
|
|
440
|
+
}
|
|
441
|
+
if ((data.globalOptions || []).length > 0) {
|
|
442
|
+
lines.push("", "Global Options:", ...formatOptionRows(data.globalOptions));
|
|
443
|
+
}
|
|
421
444
|
if ((data.details || []).length > 0) {
|
|
422
445
|
lines.push("", ...data.details);
|
|
423
446
|
}
|
package/src/index.js
CHANGED
|
@@ -19,6 +19,7 @@ import {parseConnectionArgs, ProfileError, profileCapabilityPath, readProfile, r
|
|
|
19
19
|
import {loginWithPassword, resolveLoginInputFromEnv} from "./auth.js";
|
|
20
20
|
import {executeDynamicCommand} from "./dynamic-command.js";
|
|
21
21
|
import {handleInstallSkill} from "./install-skill.js";
|
|
22
|
+
import {handleApp} from "./app.js";
|
|
22
23
|
import {cliError} from "./errors.js";
|
|
23
24
|
import {
|
|
24
25
|
renderBuiltInCommandHelp,
|
|
@@ -48,6 +49,9 @@ export async function runCli(argv, context = {}) {
|
|
|
48
49
|
if (args[0] === "install-skill") {
|
|
49
50
|
return isHelpFlagInvocation(args) ? renderBuiltInHelp("install-skill") : handleInstallSkill(args.slice(1), context);
|
|
50
51
|
}
|
|
52
|
+
if (args[0] === "app") {
|
|
53
|
+
return isHelpFlagInvocation(args) ? renderBuiltInHelp("app") : handleApp(args.slice(1), context);
|
|
54
|
+
}
|
|
51
55
|
const management = args[0] === "auth" && ["list", "use", "remove"].includes(args[1]);
|
|
52
56
|
const builtInHelp = isHelpFlagInvocation(args) && (args.length === 1 || isBuiltInResource(args[0]));
|
|
53
57
|
const optional = management || builtInHelp || args.length === 0 || args[0] === "version";
|
|
@@ -684,6 +688,7 @@ async function handleHelp(args, context) {
|
|
|
684
688
|
|
|
685
689
|
function isBuiltInResource(resourceName) {
|
|
686
690
|
return resourceName === "install-skill"
|
|
691
|
+
|| resourceName === "app"
|
|
687
692
|
|| resourceName === "config"
|
|
688
693
|
|| resourceName === "auth"
|
|
689
694
|
|| resourceName === "capability"
|
package/src/install-skill.js
CHANGED
|
@@ -1,89 +1,89 @@
|
|
|
1
|
-
import {mkdir, readFile, writeFile, rename, open, unlink} from "node:fs/promises";
|
|
2
|
-
import {existsSync} from "node:fs";
|
|
3
|
-
import {homedir} from "node:os";
|
|
4
|
-
import {join, resolve} from "node:path";
|
|
5
|
-
import {fileURLToPath} from "node:url";
|
|
6
|
-
import {createInterface} from "node:readline/promises";
|
|
7
|
-
import {agentCatalog, installSkills} from "./skill-files.js";
|
|
8
|
-
import {cliError} from "./errors.js";
|
|
9
|
-
import {failure, success} from "./output.js";
|
|
10
|
-
|
|
11
|
-
function parseOptions(args) {
|
|
12
|
-
const options = {};
|
|
13
|
-
for (let i = 0; i < args.length; i++) {
|
|
14
|
-
const arg = args[i];
|
|
15
|
-
if (arg === "--json") continue;
|
|
16
|
-
if (arg === "--list") options.list = true;
|
|
17
|
-
else if (arg === "--replace") options.replace = true;
|
|
18
|
-
else if (arg === "--agents") {
|
|
19
|
-
if (!args[i + 1] || args[i + 1].startsWith("--")) throw new Error("--agents requires comma-separated Agent IDs.");
|
|
20
|
-
options.agents = [...new Set(args[++i].split(",").map(id => id.trim()).filter(Boolean))];
|
|
21
|
-
if (!options.agents.length) throw new Error("--agents requires at least one Agent ID.");
|
|
22
|
-
} else throw new Error(`Unknown install-skill option: ${arg}`);
|
|
23
|
-
}
|
|
24
|
-
if (options.list && (options.agents || options.replace)) throw new Error("--list cannot be combined with installation options.");
|
|
25
|
-
return options;
|
|
26
|
-
}
|
|
27
|
-
|
|
28
|
-
export async function handleInstallSkill(args, context = {}) {
|
|
29
|
-
const env = context.env || process.env;
|
|
30
|
-
const home = resolve(env.USERPROFILE || env.HOME || homedir());
|
|
31
|
-
const catalog = agentCatalog({home, env});
|
|
32
|
-
let options;
|
|
33
|
-
try {
|
|
34
|
-
options = parseOptions(args);
|
|
35
|
-
if (options.list) return success("install-skill", {agents: catalog});
|
|
36
|
-
if (!options.agents) {
|
|
37
|
-
const interactive = context.isTTY ?? (process.stdin.isTTY && process.stderr.isTTY);
|
|
38
|
-
if (!interactive) throw new Error("Specify --agents <ids>; use --list to see supported Agents.");
|
|
39
|
-
const prompt = createInterface({input: process.stdin, output: process.stderr});
|
|
40
|
-
try {
|
|
41
|
-
process.stderr.write(catalog.map(a => `${a.id}: ${a.name}${a.detected ? " (detected)" : ""}`).join("\n") + "\n");
|
|
42
|
-
const answer = await prompt.question("Select Agent IDs (comma-separated): ");
|
|
43
|
-
options.agents = parseOptions(["--agents", answer]).agents;
|
|
44
|
-
} finally { prompt.close(); }
|
|
45
|
-
}
|
|
46
|
-
for (const id of options.agents) if (!catalog.some(agent => agent.id === id)) throw new Error(`Unknown Agent: ${id}`);
|
|
47
|
-
} catch (error) {
|
|
48
|
-
return failure("install-skill", cliError("INVALID_ARGUMENT", {
|
|
49
|
-
message: error.message, nextActions: ["finebi-cli install-skill --help"],
|
|
50
|
-
}));
|
|
51
|
-
}
|
|
52
|
-
|
|
53
|
-
const directory = join(home, ".finebi-cli");
|
|
54
|
-
const statePath = join(directory, "installation.json");
|
|
55
|
-
const lockPath = join(directory, "skill-install.lock");
|
|
56
|
-
let lock;
|
|
57
|
-
try {
|
|
58
|
-
const source = context.skillSource || [
|
|
59
|
-
"../skills/finebi-cli-agent/", "../../skills/finebi-cli-agent/", "../../../",
|
|
60
|
-
].map(path => fileURLToPath(new URL(path, import.meta.url)))
|
|
61
|
-
.find(path => existsSync(join(path, "SKILL.md")) && existsSync(join(path, "scripts/finebi-cli/src/index.js")));
|
|
62
|
-
if (!source) throw new Error("FineBI Skill resources were not found. Reinstall the CLI package or build the Skill in the source checkout.");
|
|
63
|
-
await readFile(join(source, "SKILL.md"));
|
|
64
|
-
await readFile(join(source, "scripts/finebi-cli/src/index.js"));
|
|
65
|
-
await mkdir(directory, {recursive: true});
|
|
66
|
-
try { lock = await open(lockPath, "wx"); }
|
|
67
|
-
catch (error) { if (error.code === "EEXIST") throw new Error(`Skill installation lock exists: ${lockPath}`); throw error; }
|
|
68
|
-
let state = {};
|
|
69
|
-
try { state = JSON.parse(await readFile(statePath, "utf8")); }
|
|
70
|
-
catch (error) { if (error.code !== "ENOENT") throw error; }
|
|
71
|
-
const results = await installSkills({source, home, env, agents: options.agents, replace: options.replace, previous: state.skills || []});
|
|
72
|
-
const skills = new Map((state.skills || []).map(skill => [skill.agent, skill]));
|
|
73
|
-
for (const skill of results) skills.set(skill.agent, {...skills.get(skill.agent), ...skill});
|
|
74
|
-
const stagedState = join(directory, "installation.json.stage");
|
|
75
|
-
await writeFile(stagedState, `${JSON.stringify({...state, skills: [...skills.values()], updatedAt: new Date().toISOString()}, null, 2)}\n`);
|
|
76
|
-
await rename(stagedState, statePath);
|
|
77
|
-
const data = {skills: results.map(({hashes, ...skill}) => skill)};
|
|
78
|
-
if (results.some(skill => !["installed", "unchanged"].includes(skill.status))) {
|
|
79
|
-
return failure("install-skill", cliError("SKILL_INSTALL_FAILED", {
|
|
80
|
-
message: "Some Skills were kept or failed. Review the results; --replace backs up and replaces local changes.", partialResult: data,
|
|
81
|
-
}));
|
|
82
|
-
}
|
|
83
|
-
return success("install-skill", data);
|
|
84
|
-
} catch (error) {
|
|
85
|
-
return failure("install-skill", cliError("SKILL_INSTALL_FAILED", {message: error.message}));
|
|
86
|
-
} finally {
|
|
87
|
-
if (lock) { await lock.close(); await unlink(lockPath); }
|
|
88
|
-
}
|
|
89
|
-
}
|
|
1
|
+
import {mkdir, readFile, writeFile, rename, open, unlink} from "node:fs/promises";
|
|
2
|
+
import {existsSync} from "node:fs";
|
|
3
|
+
import {homedir} from "node:os";
|
|
4
|
+
import {join, resolve} from "node:path";
|
|
5
|
+
import {fileURLToPath} from "node:url";
|
|
6
|
+
import {createInterface} from "node:readline/promises";
|
|
7
|
+
import {agentCatalog, installSkills} from "./skill-files.js";
|
|
8
|
+
import {cliError} from "./errors.js";
|
|
9
|
+
import {failure, success} from "./output.js";
|
|
10
|
+
|
|
11
|
+
function parseOptions(args) {
|
|
12
|
+
const options = {};
|
|
13
|
+
for (let i = 0; i < args.length; i++) {
|
|
14
|
+
const arg = args[i];
|
|
15
|
+
if (arg === "--json") continue;
|
|
16
|
+
if (arg === "--list") options.list = true;
|
|
17
|
+
else if (arg === "--replace") options.replace = true;
|
|
18
|
+
else if (arg === "--agents") {
|
|
19
|
+
if (!args[i + 1] || args[i + 1].startsWith("--")) throw new Error("--agents requires comma-separated Agent IDs.");
|
|
20
|
+
options.agents = [...new Set(args[++i].split(",").map(id => id.trim()).filter(Boolean))];
|
|
21
|
+
if (!options.agents.length) throw new Error("--agents requires at least one Agent ID.");
|
|
22
|
+
} else throw new Error(`Unknown install-skill option: ${arg}`);
|
|
23
|
+
}
|
|
24
|
+
if (options.list && (options.agents || options.replace)) throw new Error("--list cannot be combined with installation options.");
|
|
25
|
+
return options;
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
export async function handleInstallSkill(args, context = {}) {
|
|
29
|
+
const env = context.env || process.env;
|
|
30
|
+
const home = resolve(env.USERPROFILE || env.HOME || homedir());
|
|
31
|
+
const catalog = agentCatalog({home, env});
|
|
32
|
+
let options;
|
|
33
|
+
try {
|
|
34
|
+
options = parseOptions(args);
|
|
35
|
+
if (options.list) return success("install-skill", {agents: catalog});
|
|
36
|
+
if (!options.agents) {
|
|
37
|
+
const interactive = context.isTTY ?? (process.stdin.isTTY && process.stderr.isTTY);
|
|
38
|
+
if (!interactive) throw new Error("Specify --agents <ids>; use --list to see supported Agents.");
|
|
39
|
+
const prompt = createInterface({input: process.stdin, output: process.stderr});
|
|
40
|
+
try {
|
|
41
|
+
process.stderr.write(catalog.map(a => `${a.id}: ${a.name}${a.detected ? " (detected)" : ""}`).join("\n") + "\n");
|
|
42
|
+
const answer = await prompt.question("Select Agent IDs (comma-separated): ");
|
|
43
|
+
options.agents = parseOptions(["--agents", answer]).agents;
|
|
44
|
+
} finally { prompt.close(); }
|
|
45
|
+
}
|
|
46
|
+
for (const id of options.agents) if (!catalog.some(agent => agent.id === id)) throw new Error(`Unknown Agent: ${id}`);
|
|
47
|
+
} catch (error) {
|
|
48
|
+
return failure("install-skill", cliError("INVALID_ARGUMENT", {
|
|
49
|
+
message: error.message, nextActions: ["finebi-cli install-skill --help"],
|
|
50
|
+
}));
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
const directory = join(home, ".finebi-cli");
|
|
54
|
+
const statePath = join(directory, "installation.json");
|
|
55
|
+
const lockPath = join(directory, "skill-install.lock");
|
|
56
|
+
let lock;
|
|
57
|
+
try {
|
|
58
|
+
const source = context.skillSource || [
|
|
59
|
+
"../skills/finebi-cli-agent/", "../../skills/finebi-cli-agent/", "../../../",
|
|
60
|
+
].map(path => fileURLToPath(new URL(path, import.meta.url)))
|
|
61
|
+
.find(path => existsSync(join(path, "SKILL.md")) && existsSync(join(path, "scripts/finebi-cli/src/index.js")));
|
|
62
|
+
if (!source) throw new Error("FineBI Skill resources were not found. Reinstall the CLI package or build the Skill in the source checkout.");
|
|
63
|
+
await readFile(join(source, "SKILL.md"));
|
|
64
|
+
await readFile(join(source, "scripts/finebi-cli/src/index.js"));
|
|
65
|
+
await mkdir(directory, {recursive: true});
|
|
66
|
+
try { lock = await open(lockPath, "wx"); }
|
|
67
|
+
catch (error) { if (error.code === "EEXIST") throw new Error(`Skill installation lock exists: ${lockPath}`); throw error; }
|
|
68
|
+
let state = {};
|
|
69
|
+
try { state = JSON.parse(await readFile(statePath, "utf8")); }
|
|
70
|
+
catch (error) { if (error.code !== "ENOENT") throw error; }
|
|
71
|
+
const results = await installSkills({source, home, env, agents: options.agents, replace: options.replace, previous: state.skills || []});
|
|
72
|
+
const skills = new Map((state.skills || []).map(skill => [skill.agent, skill]));
|
|
73
|
+
for (const skill of results) skills.set(skill.agent, {...skills.get(skill.agent), ...skill});
|
|
74
|
+
const stagedState = join(directory, "installation.json.stage");
|
|
75
|
+
await writeFile(stagedState, `${JSON.stringify({...state, skills: [...skills.values()], updatedAt: new Date().toISOString()}, null, 2)}\n`);
|
|
76
|
+
await rename(stagedState, statePath);
|
|
77
|
+
const data = {skills: results.map(({hashes, ...skill}) => skill)};
|
|
78
|
+
if (results.some(skill => !["installed", "unchanged"].includes(skill.status))) {
|
|
79
|
+
return failure("install-skill", cliError("SKILL_INSTALL_FAILED", {
|
|
80
|
+
message: "Some Skills were kept or failed. Review the results; --replace backs up and replaces local changes.", partialResult: data,
|
|
81
|
+
}));
|
|
82
|
+
}
|
|
83
|
+
return success("install-skill", data);
|
|
84
|
+
} catch (error) {
|
|
85
|
+
return failure("install-skill", cliError("SKILL_INSTALL_FAILED", {message: error.message}));
|
|
86
|
+
} finally {
|
|
87
|
+
if (lock) { await lock.close(); await unlink(lockPath); }
|
|
88
|
+
}
|
|
89
|
+
}
|