@finebi/finebi-cli 1.2.1 → 1.4.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 +63 -39
- package/package.json +40 -37
- package/skills/finebi-cli-agent/SKILL.md +133 -0
- package/skills/finebi-cli-agent/agents/openai.yaml +7 -0
- package/skills/finebi-cli-agent/references/analysis-dsl-api.md +322 -0
- package/skills/finebi-cli-agent/references/artifact-ordering.md +29 -0
- package/skills/finebi-cli-agent/references/command-workflows.md +199 -0
- package/skills/finebi-cli-agent/references/dashboard-dsl-api.md +1037 -0
- package/skills/finebi-cli-agent/references/dsl-handoff-contract.md +195 -0
- package/skills/finebi-cli-agent/references/file-to-dashboard.md +65 -0
- package/skills/finebi-cli-agent/references/formula-basics.md +102 -0
- package/skills/finebi-cli-agent/references/formula-contexts.md +66 -0
- package/skills/finebi-cli-agent/references/formula-functions.md +247 -0
- package/skills/finebi-cli-agent/references/formula-generation.md +121 -0
- package/skills/finebi-cli-agent/references/formula-glossary.md +17 -0
- package/skills/finebi-cli-agent/references/formula-help-boundaries.md +87 -0
- package/skills/finebi-cli-agent/references/formula-help-index.md +264 -0
- package/skills/finebi-cli-agent/references/formula-parameters-aggregate.md +143 -0
- package/skills/finebi-cli-agent/references/formula-parameters-analysis.md +223 -0
- package/skills/finebi-cli-agent/references/formula-parameters-date.md +236 -0
- package/skills/finebi-cli-agent/references/formula-parameters-math-logic.md +271 -0
- package/skills/finebi-cli-agent/references/formula-parameters-text.md +217 -0
- package/skills/finebi-cli-agent/references/formula-recipes-analysis.md +124 -0
- package/skills/finebi-cli-agent/references/formula-recipes-text-date.md +86 -0
- package/skills/finebi-cli-agent/references/formula-semantics.md +39 -0
- package/skills/finebi-cli-agent/references/formula-syntax.md +70 -0
- package/skills/finebi-cli-agent/references/formula-troubleshooting.md +63 -0
- package/skills/finebi-cli-agent/references/formula-validation.md +36 -0
- package/skills/finebi-cli-agent/references/formula.md +37 -0
- package/skills/finebi-cli-agent/references/handoff-contract.md +210 -0
- package/skills/finebi-cli-agent/references/mapping-rules.md +423 -0
- package/skills/finebi-cli-agent/references/mutation-confirmation-contract.md +150 -0
- package/skills/finebi-cli-agent/references/scenario-workflows.md +75 -0
- package/skills/finebi-cli-agent/references/widget-dsl-api-examples.md +457 -0
- package/skills/finebi-cli-agent/references/widget-dsl-api.md +1340 -0
- package/skills/finebi-cli-agent/scripts/finebi-cli/package.json +11 -0
- package/skills/finebi-cli-agent/scripts/finebi-cli/src/access-key-contract.js +2 -0
- package/skills/finebi-cli-agent/scripts/finebi-cli/src/access-key.js +174 -0
- package/skills/finebi-cli-agent/scripts/finebi-cli/src/auth-failure.js +18 -0
- package/skills/finebi-cli-agent/scripts/finebi-cli/src/auth-headers.js +21 -0
- package/skills/finebi-cli-agent/scripts/finebi-cli/src/auth.js +116 -0
- package/skills/finebi-cli-agent/scripts/finebi-cli/src/branding.js +31 -0
- package/skills/finebi-cli-agent/scripts/finebi-cli/src/browser-auth.js +331 -0
- package/skills/finebi-cli-agent/scripts/finebi-cli/src/capability.js +122 -0
- package/skills/finebi-cli-agent/scripts/finebi-cli/src/config-permissions.js +87 -0
- package/skills/finebi-cli-agent/scripts/finebi-cli/src/config.js +189 -0
- package/skills/finebi-cli-agent/scripts/finebi-cli/src/dynamic-command.js +324 -0
- package/skills/finebi-cli-agent/scripts/finebi-cli/src/error-catalog.js +482 -0
- package/skills/finebi-cli-agent/scripts/finebi-cli/src/errors.js +22 -0
- package/skills/finebi-cli-agent/scripts/finebi-cli/src/help.js +467 -0
- package/skills/finebi-cli-agent/scripts/finebi-cli/src/index.js +1351 -0
- package/skills/finebi-cli-agent/scripts/finebi-cli/src/install-skill.js +89 -0
- package/skills/finebi-cli-agent/scripts/finebi-cli/src/output-limits.js +188 -0
- package/skills/finebi-cli-agent/scripts/finebi-cli/src/output.js +33 -0
- package/skills/finebi-cli-agent/scripts/finebi-cli/src/profiles.js +158 -0
- package/skills/finebi-cli-agent/scripts/finebi-cli/src/schema-options.js +265 -0
- package/skills/finebi-cli-agent/scripts/finebi-cli/src/schema-validator.js +194 -0
- package/skills/finebi-cli-agent/scripts/finebi-cli/src/skill-files.js +94 -0
- package/skills/finebi-cli-agent/scripts/finebi-cli-runner-core.mjs +399 -0
- package/skills/finebi-cli-agent/scripts/finebi-cli-runner.mjs +29 -0
- package/src/config.js +27 -27
- package/src/error-catalog.js +12 -0
- package/src/help.js +70 -55
- package/src/index.js +17 -4
- package/src/install-skill.js +89 -0
- package/src/profiles.js +158 -158
- package/src/skill-files.js +94 -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,20 @@ function findResource(manifest, resourceName) {
|
|
|
99
99
|
}
|
|
100
100
|
|
|
101
101
|
const BUILT_IN_RESOURCES = {
|
|
102
|
+
"install-skill": {
|
|
103
|
+
command: "install-skill",
|
|
104
|
+
resource: "install-skill",
|
|
105
|
+
summary: "Install or update the bundled FineBI Skill in Agent directories without a server connection.",
|
|
106
|
+
usage: "finebi-cli install-skill [--agents <ids>] [--replace]",
|
|
107
|
+
options: [
|
|
108
|
+
{name: "--agents", type: "string", description: "Comma-separated Agent IDs; required outside an interactive terminal."},
|
|
109
|
+
{name: "--list", type: "boolean", description: "List supported Agents and their installation directories without writing files."},
|
|
110
|
+
{name: "--replace", type: "boolean", description: "Back up and replace locally modified or untracked Skill installations."},
|
|
111
|
+
],
|
|
112
|
+
details: ["Unmodified managed installations update automatically with a backup. Local changes are kept by default.",
|
|
113
|
+
"No server credentials are needed. Restart your Agent or refresh its Skills after installation."],
|
|
114
|
+
examples: ["finebi-cli install-skill --agents codex,cursor", "finebi-cli install-skill --list"],
|
|
115
|
+
},
|
|
102
116
|
version: {
|
|
103
117
|
command: "version",
|
|
104
118
|
resource: "version",
|
|
@@ -138,7 +152,7 @@ const BUILT_IN_RESOURCES = {
|
|
|
138
152
|
},
|
|
139
153
|
{
|
|
140
154
|
name: "clear",
|
|
141
|
-
summary: "Remove the selected environment's local configuration, credentials and capability cache; does not revoke server keys.",
|
|
155
|
+
summary: "Remove the selected environment's local configuration, credentials and capability cache; does not revoke server keys.",
|
|
142
156
|
usage: "finebi-cli config clear",
|
|
143
157
|
},
|
|
144
158
|
],
|
|
@@ -150,18 +164,18 @@ const BUILT_IN_RESOURCES = {
|
|
|
150
164
|
{
|
|
151
165
|
name: "login",
|
|
152
166
|
summary: "Login with FineBI credentials or browser authorization and persist an Access Key.",
|
|
153
|
-
usage: "finebi-cli auth login [--profile <name>] [--base-url <url>] [--browser] [--username <username>] [--from-env] [--password-stdin] [--no-use]",
|
|
154
|
-
options: [
|
|
155
|
-
{ name: "--no-use", type: "boolean", description: "Save successful login without changing the default environment." },
|
|
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." },
|
|
156
170
|
{ name: "--username", type: "string", description: "FineBI username." },
|
|
157
171
|
{ name: "--from-env", type: "boolean", description: "Read base URL, username, and password from environment variables." },
|
|
158
172
|
{ name: "--password-stdin", type: "boolean", description: "Read password from standard input." },
|
|
159
173
|
{ name: "--browser", type: "boolean", description: "Authorize this CLI from a signed-in FineBI browser without sending a password." },
|
|
160
174
|
],
|
|
161
|
-
details: [
|
|
162
|
-
"Login creates the named environment if missing and selects it after successful authentication, unless --no-use is supplied.",
|
|
163
|
-
"A new environment requires --base-url or FINEBI_BASE_URL; it never inherits another environment's address or identity.",
|
|
164
|
-
"Failed authentication preserves saved environments and the default selection.",
|
|
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.",
|
|
165
179
|
"Environment variables for --from-env:",
|
|
166
180
|
" FINEBI_BASE_URL",
|
|
167
181
|
" FINEBI_USERNAME",
|
|
@@ -173,28 +187,28 @@ const BUILT_IN_RESOURCES = {
|
|
|
173
187
|
"finebi-cli auth login --username <username>",
|
|
174
188
|
"finebi-cli auth login --from-env",
|
|
175
189
|
"echo <password> | finebi-cli auth login --username <username> --password-stdin",
|
|
176
|
-
"finebi-cli auth login --browser",
|
|
177
|
-
"finebi-cli auth login --profile test --base-url https://test.example.com/webroot/decision --browser",
|
|
190
|
+
"finebi-cli auth login --browser",
|
|
191
|
+
"finebi-cli auth login --profile test --base-url https://test.example.com/webroot/decision --browser",
|
|
178
192
|
],
|
|
179
193
|
},
|
|
180
|
-
{
|
|
181
|
-
name: "list",
|
|
182
|
-
summary: "List saved environments and mark the default; local metadata only, no credential validation or secret output.",
|
|
183
|
-
usage: "finebi-cli auth list",
|
|
184
|
-
},
|
|
185
|
-
{
|
|
186
|
-
name: "use",
|
|
187
|
-
summary: "Select an existing environment as default without logging in.",
|
|
188
|
-
usage: "finebi-cli auth use <name>",
|
|
189
|
-
},
|
|
190
|
-
{
|
|
191
|
-
name: "remove",
|
|
192
|
-
summary: "Delete one local environment and its capability cache; does not revoke server keys.",
|
|
193
|
-
usage: "finebi-cli auth remove <name>",
|
|
194
|
-
details: ["Use auth logout --profile <name> first to revoke its Access Key.", "Removing the default environment does not select another environment."],
|
|
195
|
-
},
|
|
196
|
-
{
|
|
197
|
-
name: "info",
|
|
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",
|
|
198
212
|
summary: "Show stored authentication metadata with secrets masked.",
|
|
199
213
|
usage: "finebi-cli auth info",
|
|
200
214
|
},
|
|
@@ -344,6 +358,7 @@ function renderTopLevelHelpText(data) {
|
|
|
344
358
|
"",
|
|
345
359
|
"Common Commands:",
|
|
346
360
|
` ${commandLine("version")}`,
|
|
361
|
+
` ${commandLine("install-skill", "--agents", "codex")}`,
|
|
347
362
|
` ${commandLine("capabilities")}`,
|
|
348
363
|
` ${commandLine("config", "get")}`,
|
|
349
364
|
` ${commandLine("config", "set", "base-url", "<baseUrl>")}`,
|
|
@@ -353,10 +368,10 @@ function renderTopLevelHelpText(data) {
|
|
|
353
368
|
"",
|
|
354
369
|
"Use --help or -h for command details.",
|
|
355
370
|
"Use --json with --help for structured output.",
|
|
356
|
-
"Output options: --select <paths> / --max-chars <n> / --save <path> for large responses.",
|
|
357
|
-
"Environment: --profile <name> > FINEBI_PROFILE > saved default. Global options may precede or follow the command.",
|
|
358
|
-
);
|
|
359
|
-
lines.push("", "Global Options:", ...formatOptionRows(data.globalOptions || []));
|
|
371
|
+
"Output options: --select <paths> / --max-chars <n> / --save <path> for large responses.",
|
|
372
|
+
"Environment: --profile <name> > FINEBI_PROFILE > saved default. Global options may precede or follow the command.",
|
|
373
|
+
);
|
|
374
|
+
lines.push("", "Global Options:", ...formatOptionRows(data.globalOptions || []));
|
|
360
375
|
return `${lines.join("\n")}\n`;
|
|
361
376
|
}
|
|
362
377
|
|
|
@@ -372,8 +387,8 @@ function renderResourceHelpText(data) {
|
|
|
372
387
|
"",
|
|
373
388
|
"Commands:",
|
|
374
389
|
];
|
|
375
|
-
lines.push(...formatRows(data.commands || [], "name", "summary"));
|
|
376
|
-
lines.push("", "Global Options:", ...formatOptionRows(data.globalOptions || []));
|
|
390
|
+
lines.push(...formatRows(data.commands || [], "name", "summary"));
|
|
391
|
+
lines.push("", "Global Options:", ...formatOptionRows(data.globalOptions || []));
|
|
377
392
|
lines.push("", `Use ${commandLine(data.resource, "<command>", "--help")} for command details.`);
|
|
378
393
|
return `${lines.join("\n")}\n`;
|
|
379
394
|
}
|
|
@@ -388,13 +403,13 @@ function renderCommandHelpText(data) {
|
|
|
388
403
|
"Usage:",
|
|
389
404
|
` ${data.usage}`,
|
|
390
405
|
];
|
|
391
|
-
if ((data.options || []).length > 0) {
|
|
406
|
+
if ((data.options || []).length > 0) {
|
|
392
407
|
lines.push("", "Options:");
|
|
393
408
|
lines.push(...formatOptionRows(data.options));
|
|
394
|
-
}
|
|
395
|
-
if ((data.globalOptions || []).length > 0) {
|
|
396
|
-
lines.push("", "Global Options:", ...formatOptionRows(data.globalOptions));
|
|
397
|
-
}
|
|
409
|
+
}
|
|
410
|
+
if ((data.globalOptions || []).length > 0) {
|
|
411
|
+
lines.push("", "Global Options:", ...formatOptionRows(data.globalOptions));
|
|
412
|
+
}
|
|
398
413
|
if ((data.details || []).length > 0) {
|
|
399
414
|
lines.push("", ...data.details);
|
|
400
415
|
}
|
package/src/index.js
CHANGED
|
@@ -18,6 +18,7 @@ import {defaultConfigPath, maskConfig, resolveConfig, sameBaseUrl, updateConfigV
|
|
|
18
18
|
import {parseConnectionArgs, ProfileError, profileCapabilityPath, readProfile, readProfiles, removeProfile, saveProfile, selectProfile, useProfile, withProfileLoginLock} from "./profiles.js";
|
|
19
19
|
import {loginWithPassword, resolveLoginInputFromEnv} from "./auth.js";
|
|
20
20
|
import {executeDynamicCommand} from "./dynamic-command.js";
|
|
21
|
+
import {handleInstallSkill} from "./install-skill.js";
|
|
21
22
|
import {cliError} from "./errors.js";
|
|
22
23
|
import {
|
|
23
24
|
renderBuiltInCommandHelp,
|
|
@@ -44,11 +45,17 @@ export async function runCli(argv, context = {}) {
|
|
|
44
45
|
try {
|
|
45
46
|
const {args, flags} = parseConnectionArgs(argv);
|
|
46
47
|
const env = context.env || process.env;
|
|
48
|
+
if (args[0] === "install-skill") {
|
|
49
|
+
return isHelpFlagInvocation(args) ? renderBuiltInHelp("install-skill") : handleInstallSkill(args.slice(1), context);
|
|
50
|
+
}
|
|
47
51
|
const management = args[0] === "auth" && ["list", "use", "remove"].includes(args[1]);
|
|
48
52
|
const builtInHelp = isHelpFlagInvocation(args) && (args.length === 1 || isBuiltInResource(args[0]));
|
|
49
53
|
const optional = management || builtInHelp || args.length === 0 || args[0] === "version";
|
|
50
|
-
const
|
|
51
|
-
|
|
54
|
+
const requestedProfile = firstNonEmpty(flags.profile, env.FINEBI_PROFILE);
|
|
55
|
+
const allowCreate = args[0] === "auth" && args[1] === "login";
|
|
56
|
+
const initialDefault = !requestedProfile && !existsSync(configPath(context));
|
|
57
|
+
const profileName = management ? null : selectProfile(configPath(context), requestedProfile, {
|
|
58
|
+
allowCreate, optional,
|
|
52
59
|
});
|
|
53
60
|
if (management && (flags.profile || env.FINEBI_PROFILE) && args[1] !== "list") {
|
|
54
61
|
if (flags.profile && flags.profile !== args[2]) throw new ProfileError("Use the positional environment name for auth use/remove; --profile must not select a different environment.");
|
|
@@ -60,7 +67,12 @@ export async function runCli(argv, context = {}) {
|
|
|
60
67
|
);
|
|
61
68
|
const envelope = mutatesProfile
|
|
62
69
|
? await withProfileLoginLock(configPath(context), args[0] === "auth" && args[1] === "remove" ? args[2] : profileName,
|
|
63
|
-
() =>
|
|
70
|
+
() => {
|
|
71
|
+
if (!management) selectProfile(configPath(context), profileName, {
|
|
72
|
+
allowCreate: allowCreate || (initialDefault && profileName === "default"),
|
|
73
|
+
});
|
|
74
|
+
return dispatchCli(args, context);
|
|
75
|
+
})
|
|
64
76
|
: await dispatchCli(args, context);
|
|
65
77
|
if (profileName) {
|
|
66
78
|
envelope.meta = {...envelope.meta, profile: profileName};
|
|
@@ -671,7 +683,8 @@ async function handleHelp(args, context) {
|
|
|
671
683
|
}
|
|
672
684
|
|
|
673
685
|
function isBuiltInResource(resourceName) {
|
|
674
|
-
return resourceName === "
|
|
686
|
+
return resourceName === "install-skill"
|
|
687
|
+
|| resourceName === "config"
|
|
675
688
|
|| resourceName === "auth"
|
|
676
689
|
|| resourceName === "capability"
|
|
677
690
|
|| resourceName === "version"
|
|
@@ -0,0 +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
|
+
}
|