@jfrog/opencode-jfrog-plugin 0.0.2 → 0.0.4
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 +105 -51
- package/dist/index.js +30 -238
- package/package.json +6 -6
- package/skills/jfrog/SKILL.md +529 -0
- package/skills/jfrog/assets/.gitkeep +0 -0
- package/skills/jfrog/references/apptrust-entities.md +154 -0
- package/skills/jfrog/references/artifactory-api-gaps.md +206 -0
- package/skills/jfrog/references/artifactory-aql-syntax.md +656 -0
- package/skills/jfrog/references/artifactory-entities.md +236 -0
- package/skills/jfrog/references/artifactory-operations.md +178 -0
- package/skills/jfrog/references/catalog-entities.md +219 -0
- package/skills/jfrog/references/general-bulk-operations-and-agent-patterns.md +93 -0
- package/skills/jfrog/references/general-parallel-execution.md +131 -0
- package/skills/jfrog/references/general-use-case-hints.md +27 -0
- package/skills/jfrog/references/jfrog-brand-html-report.md +98 -0
- package/skills/jfrog/references/jfrog-cli-install-upgrade.md +30 -0
- package/skills/jfrog/references/jfrog-entity-index.md +112 -0
- package/skills/jfrog/references/jfrog-login-flow.md +132 -0
- package/skills/jfrog/references/jfrog-url-references.md +51 -0
- package/skills/jfrog/references/onemodel-common-patterns.md +323 -0
- package/skills/jfrog/references/onemodel-graphql.md +446 -0
- package/skills/jfrog/references/onemodel-query-examples.md +753 -0
- package/skills/jfrog/references/platform-access-entities.md +200 -0
- package/skills/jfrog/references/platform-admin-api-gaps.md +164 -0
- package/skills/jfrog/references/platform-admin-operations.md +58 -0
- package/skills/jfrog/references/projects-api.md +241 -0
- package/skills/jfrog/references/release-lifecycle-entities.md +180 -0
- package/skills/jfrog/references/stored-packages-entities.md +165 -0
- package/skills/jfrog/references/xray-entities.md +740 -0
- package/skills/jfrog/scripts/check-environment.sh +224 -0
- package/skills/jfrog/scripts/jfrog-login-register-session.sh +84 -0
- package/skills/jfrog/scripts/jfrog-login-save-credentials.sh +128 -0
- package/skills/jfrog-package-safety-and-download/SKILL.md +275 -0
- package/sync-skills-vendor.json +5 -0
package/README.md
CHANGED
|
@@ -1,82 +1,136 @@
|
|
|
1
1
|
# opencode-jfrog-plugin
|
|
2
2
|
|
|
3
|
-
JFrog
|
|
4
|
-
|
|
3
|
+
JFrog integration for [OpenCode](https://opencode.ai/). The plugin ships the official JFrog
|
|
4
|
+
[Agent Skills](https://opencode.ai/docs/skills/) with the package and registers them with OpenCode at
|
|
5
|
+
load time, so JFrog capabilities are available to the agent out of the box.
|
|
5
6
|
|
|
6
|
-
##
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
-
|
|
7
|
+
## What's included
|
|
8
|
+
|
|
9
|
+
The plugin bundles two canonical skills, vendored (pinned) from
|
|
10
|
+
[`jfrog/jfrog-skills`](https://github.com/jfrog/jfrog-skills) and committed under `skills/`:
|
|
10
11
|
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
-
|
|
12
|
+
- **`jfrog`** — interact with the JFrog Platform via the JFrog CLI, MCP server, and REST/GraphQL APIs
|
|
13
|
+
(Artifactory, Xray, builds, permissions, projects, release lifecycle, advanced security, and more).
|
|
14
|
+
- **`jfrog-package-safety-and-download`** — check package safety/curation status and download packages
|
|
15
|
+
through JFrog.
|
|
14
16
|
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
- Xray deployment
|
|
18
|
-
- Curation configuration on the used remote repositories
|
|
17
|
+
The skills ship **with the plugin** (vendored and pinned). They are **not** downloaded at runtime, so
|
|
18
|
+
the plugin works offline and the skill set is reproducible for a given plugin version.
|
|
19
19
|
|
|
20
|
+
The plugin is **self-contained**: everything it needs is in the published npm tarball (`dist/` + the
|
|
21
|
+
vendored `skills/`). There are no runtime downloads and no dependency on `releases.jfrog.io` or any other
|
|
22
|
+
external artifact host.
|
|
23
|
+
|
|
24
|
+
## Prerequisites
|
|
20
25
|
|
|
21
|
-
|
|
26
|
+
- A [JFrog Platform](https://jfrog.com) instance you can authenticate against.
|
|
27
|
+
- [OpenCode](https://opencode.ai/) installed (verified against OpenCode **1.17.7** and newer, which
|
|
28
|
+
honors `config.skills.paths` in object form).
|
|
29
|
+
- For running the skills at runtime, the following must be on your `PATH`:
|
|
30
|
+
- [`jf`](https://jfrog.com/getting-started-with-jfrog-cli/) (JFrog CLI), `jq`, and `curl`.
|
|
31
|
+
- A configured JFrog CLI server (e.g. via `jf login` / `jf config add`).
|
|
22
32
|
|
|
23
33
|
## Installation
|
|
24
|
-
Add the opencode-jfrog-plugin into your opencode config.
|
|
25
|
-
Preferably set the plugin globally for all their developers using the Opencode remote configuration, visit [opencode remote configuration](https://opencode.ai/docs/config/#remote) for more details.
|
|
26
34
|
|
|
27
|
-
The plugin
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
35
|
+
The plugin is published to public npm as
|
|
36
|
+
[`@jfrog/opencode-jfrog-plugin`](https://www.npmjs.com/package/@jfrog/opencode-jfrog-plugin) and is
|
|
37
|
+
listed on the [OpenCode ecosystem page](https://opencode.ai/docs/ecosystem). OpenCode has no plugin
|
|
38
|
+
marketplace — you install by referencing the npm package in your OpenCode config.
|
|
39
|
+
|
|
40
|
+
Add the plugin to your OpenCode config (`opencode.json`):
|
|
41
|
+
|
|
42
|
+
```json
|
|
43
|
+
{
|
|
44
|
+
"plugin": ["@jfrog/opencode-jfrog-plugin"]
|
|
45
|
+
}
|
|
32
46
|
```
|
|
33
47
|
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
48
|
+
OpenCode resolves the package from npm and loads it. To pin a specific version, use
|
|
49
|
+
`"@jfrog/opencode-jfrog-plugin@<version>"`; omitting the version tracks the latest release.
|
|
50
|
+
|
|
51
|
+
For an organization-wide rollout, set the plugin in OpenCode's
|
|
52
|
+
[remote configuration](https://opencode.ai/docs/config/#remote) so every developer gets it
|
|
53
|
+
automatically.
|
|
39
54
|
|
|
40
|
-
|
|
41
|
-
Once the user tries to resolve dependencies, handle packages, install MCP servers, and pull a skill, the system will verify integration prerequisites (JFrog CLI is installed and configured and `<project-root>/.jfrog/local/package-managers.json` is available) and will perform the task using JFrog capabilities.
|
|
55
|
+
## How it works
|
|
42
56
|
|
|
43
|
-
|
|
57
|
+
The plugin is intentionally **thin**. On load it:
|
|
44
58
|
|
|
45
|
-
|
|
46
|
-
|
|
59
|
+
1. Resolves its bundled `skills/` directory (shipped inside the package).
|
|
60
|
+
2. Registers that directory with OpenCode through the `config` hook by adding it to
|
|
61
|
+
`config.skills.paths`.
|
|
47
62
|
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
### Automatically
|
|
52
|
-
Within opencode, when tasks require a JFrog skill, the triggered skill will guide the user through the needed setup.
|
|
63
|
+
OpenCode then discovers the skills the same way it discovers any skill — they appear via the `skill`
|
|
64
|
+
tool and `/skills`, and the agent invokes them when relevant. There is no runtime download, unzip, or
|
|
65
|
+
network call on load.
|
|
53
66
|
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
67
|
+
## Updating the bundled skills
|
|
68
|
+
|
|
69
|
+
The skills are vendored at a pinned version. Updating them is a build-time step and **requires a new
|
|
70
|
+
plugin release** (there are no runtime skill updates). See [VENDOR.md](./VENDOR.md) for the pin-bump
|
|
71
|
+
workflow (`mise run sync-skills`).
|
|
58
72
|
|
|
59
73
|
## Troubleshooting
|
|
60
|
-
|
|
61
|
-
The plugin does not log by default
|
|
62
|
-
|
|
74
|
+
|
|
75
|
+
The plugin does not log by default. To enable debug logging:
|
|
76
|
+
|
|
77
|
+
```bash
|
|
78
|
+
export JFROG_DEBUG_LOGS=true
|
|
79
|
+
```
|
|
80
|
+
|
|
81
|
+
Logs are written to `<project-root>/.opencode/event-log.txt`.
|
|
82
|
+
|
|
83
|
+
If you see a **"bundled skills not found"** error (a toast in the TUI and/or an `ERROR` line in the log),
|
|
84
|
+
the installed package is incomplete or corrupted — reinstall `@jfrog/opencode-jfrog-plugin`.
|
|
85
|
+
|
|
86
|
+
## Upgrading from < 0.0.3
|
|
87
|
+
|
|
88
|
+
This release changes behavior in ways that are **not** backward compatible:
|
|
89
|
+
|
|
90
|
+
- **Skill catalog changed (7 → 2).** The previous Artifactory skills — `skill-install`,
|
|
91
|
+
`skill-publish`, `jfrog-cli`, `opencode-jfrog-mcp`, `jfrog-setup-package-managers`, `jfrog-curation`,
|
|
92
|
+
`jfrog-packages` — are replaced by the two canonical skills above. Invocations of the removed skill
|
|
93
|
+
names no longer exist; that functionality now folds into the `jfrog` skill.
|
|
94
|
+
- **Package-manager auto-setup was removed.** Earlier versions ran `jf setup <pm>` automatically on
|
|
95
|
+
session start. That is gone; the plugin now emits an interim one-line nudge to run
|
|
96
|
+
`jf setup <pm>` yourself. Durable package-manager setup is being recovered upstream in
|
|
97
|
+
`jfrog/jfrog-skills`.
|
|
98
|
+
- **Old skills are not auto-cleaned.** The plugin no longer touches `~/.config/opencode/skills`. If you
|
|
99
|
+
used a version < 0.0.3, remove the old managed skill directories yourself (e.g. `skill-install`,
|
|
100
|
+
`skill-publish`, `jfrog-cli`, `opencode-jfrog-mcp`, `jfrog-setup-package-managers`, `jfrog-curation`,
|
|
101
|
+
`jfrog-packages`) under `~/.config/opencode/skills`.
|
|
102
|
+
- **No more runtime artifacts.** The plugin no longer injects an instructions file
|
|
103
|
+
(`.jfrog/instructions/...`) or writes `.jfrog/local/package-managers.json`, and it no longer
|
|
104
|
+
downloads skills at runtime.
|
|
105
|
+
- **Dependencies resolve from public npm.** Internal registry references were removed; the build and CI
|
|
106
|
+
now resolve from public npm.
|
|
63
107
|
|
|
64
108
|
## Development
|
|
65
109
|
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
- `mise run
|
|
69
|
-
- `mise run
|
|
70
|
-
- `mise run
|
|
110
|
+
Tasks are run with [mise](https://mise.jdx.dev/):
|
|
111
|
+
|
|
112
|
+
- `mise run build` — build the module
|
|
113
|
+
- `mise run test` — run tests (`bun test`)
|
|
114
|
+
- `mise run typecheck` — type-check with `tsc --noEmit`
|
|
115
|
+
- `mise run lint` — lint with ESLint
|
|
116
|
+
- `mise run lint:fix` — auto-fix lint issues
|
|
117
|
+
- `mise run format` — format with Prettier
|
|
118
|
+
- `mise run sync-skills` — re-vendor the bundled skills (see [VENDOR.md](./VENDOR.md))
|
|
71
119
|
|
|
72
120
|
## Release
|
|
73
121
|
|
|
74
|
-
See
|
|
122
|
+
See [RELEASE.md](./RELEASE.md) for how to release a new version.
|
|
75
123
|
|
|
76
124
|
## Contributing
|
|
77
125
|
|
|
78
|
-
Contributions are welcome! Please file issues or
|
|
126
|
+
Contributions are welcome! See [CONTRIBUTING.md](./CONTRIBUTING.md). Please file issues or open pull
|
|
127
|
+
requests on the GitHub repository.
|
|
79
128
|
|
|
80
129
|
## License
|
|
81
130
|
|
|
82
|
-
See the [LICENSE](LICENSE) file for details.
|
|
131
|
+
See the [LICENSE](./LICENSE) file for details.
|
|
132
|
+
|
|
133
|
+
## Compatibility
|
|
134
|
+
|
|
135
|
+
Verified against OpenCode **1.17.7** and newer (the first version confirmed to honor
|
|
136
|
+
`config.skills.paths` in object form). Older versions are not supported.
|
package/dist/index.js
CHANGED
|
@@ -1,224 +1,21 @@
|
|
|
1
1
|
// @bun
|
|
2
2
|
// src/index.ts
|
|
3
|
-
import {
|
|
4
|
-
appendFileSync,
|
|
5
|
-
createWriteStream,
|
|
6
|
-
readFileSync,
|
|
7
|
-
existsSync,
|
|
8
|
-
mkdirSync,
|
|
9
|
-
readdirSync,
|
|
10
|
-
rmSync,
|
|
11
|
-
statSync
|
|
12
|
-
} from "fs";
|
|
13
|
-
import { Readable } from "stream";
|
|
14
|
-
import { pipeline } from "stream/promises";
|
|
3
|
+
import { appendFileSync, existsSync, mkdirSync, readdirSync, statSync } from "fs";
|
|
15
4
|
import { dirname, join } from "path";
|
|
5
|
+
import { fileURLToPath } from "url";
|
|
16
6
|
var LOG_FILE = join(process.cwd(), ".opencode", "event-log.txt");
|
|
17
|
-
var
|
|
18
|
-
var
|
|
19
|
-
var SKILLS_TO_INSTALL_URL = "https://releases.jfrog.io/artifactory/run/ai/integrations/opencode/JFROG-OPENCODE_SKILLS.json";
|
|
20
|
-
var fetchAndSaveFile = async (url, destPath, log) => {
|
|
21
|
-
const dir = dirname(destPath);
|
|
22
|
-
log("Fetching file from " + url + " and saving to " + destPath);
|
|
7
|
+
var BUNDLED_SKILLS_DIR = join(dirname(fileURLToPath(import.meta.url)), "..", "skills");
|
|
8
|
+
var isNonEmptyDir = (dir) => {
|
|
23
9
|
if (!existsSync(dir)) {
|
|
24
|
-
|
|
25
|
-
mkdirSync(dir, { recursive: true });
|
|
10
|
+
return false;
|
|
26
11
|
}
|
|
27
|
-
const response = await fetch(url);
|
|
28
|
-
if (!response.ok || !response.body) {
|
|
29
|
-
log(`Failed to fetch file from ${url}, status: ${response.status}, No response body from ${url}, response: ${JSON.stringify(response)}`);
|
|
30
|
-
return { success: false, error: `No response body from ${url}` };
|
|
31
|
-
}
|
|
32
|
-
const writer = createWriteStream(destPath);
|
|
33
|
-
await pipeline(Readable.fromWeb(response.body), writer);
|
|
34
|
-
return { success: true, error: undefined };
|
|
35
|
-
};
|
|
36
|
-
var extractZip = async ($, skillZipFile, skillName, skillVersion, skillZipDir, log) => {
|
|
37
|
-
const unzipResponse = await $`unzip -o ${skillZipFile} -d ${skillZipDir}`.nothrow().quiet();
|
|
38
|
-
if (unzipResponse.exitCode !== 0) {
|
|
39
|
-
log(`Failed to extract JFrog ${skillName} skill: ${unzipResponse.stderr}`);
|
|
40
|
-
return {
|
|
41
|
-
success: false,
|
|
42
|
-
error: `Failed to extract JFrog ${skillName}-${skillVersion} skill: ${unzipResponse.stderr}`
|
|
43
|
-
};
|
|
44
|
-
}
|
|
45
|
-
log(`JFrog ${skillName}-${skillVersion} skill extracted!`);
|
|
46
|
-
await $`rm -fR ${skillZipFile}`;
|
|
47
|
-
log(`Jfrog ${skillName} skill zip file removed!`);
|
|
48
|
-
return { success: true };
|
|
49
|
-
};
|
|
50
|
-
var setupPackageManagers = async (client, $, directory, sessionId, log) => {
|
|
51
|
-
let jfVersion;
|
|
52
12
|
try {
|
|
53
|
-
|
|
54
|
-
} catch
|
|
55
|
-
|
|
56
|
-
return {
|
|
57
|
-
success: false,
|
|
58
|
-
message: "Jfrog cli is not installed, please use the jfrog-cli skill to install it"
|
|
59
|
-
};
|
|
60
|
-
}
|
|
61
|
-
if (!jfVersion || jfVersion.exitCode !== 0) {
|
|
62
|
-
log("jf version command failed");
|
|
63
|
-
return {
|
|
64
|
-
success: false,
|
|
65
|
-
message: "Jfrog cli is not installed, please use the jfrog-cli skill to install it"
|
|
66
|
-
};
|
|
67
|
-
}
|
|
68
|
-
const packageManagersFile = join(directory, ".jfrog", "local", "package-managers.json");
|
|
69
|
-
if (!existsSync(packageManagersFile)) {
|
|
70
|
-
return {
|
|
71
|
-
success: false,
|
|
72
|
-
message: "Jfrog packages are not setup, please use the jfrog-setup-package-managers skill to complete setup. type /skill and select jfrog-setup-package-managers"
|
|
73
|
-
};
|
|
74
|
-
}
|
|
75
|
-
const packageManagersConfig = JSON.parse(readFileSync(packageManagersFile, "utf8"));
|
|
76
|
-
const configuredPackageManagers = packageManagersConfig.configuredPackageManagers;
|
|
77
|
-
if (!configuredPackageManagers) {
|
|
78
|
-
return {
|
|
79
|
-
success: false,
|
|
80
|
-
message: "Jfrog packages are not setup, please use the jfrog-setup-package-managers skill to complete setup. type /skill and select jfrog-cli"
|
|
81
|
-
};
|
|
82
|
-
}
|
|
83
|
-
const results = { success: [], error: [] };
|
|
84
|
-
for (const packageManager in configuredPackageManagers) {
|
|
85
|
-
const packageManagerConfig = configuredPackageManagers[packageManager];
|
|
86
|
-
const result = await $`jf setup ${packageManager} --server-id ${packageManagerConfig.serverId} --repo ${packageManagerConfig.repository}`.nothrow().quiet();
|
|
87
|
-
if (result.exitCode !== 0) {
|
|
88
|
-
results.error.push({ packageManager, error: result.stderr });
|
|
89
|
-
} else {
|
|
90
|
-
results.success.push({ packageManager });
|
|
91
|
-
}
|
|
13
|
+
return statSync(dir).isDirectory() && readdirSync(dir).length > 0;
|
|
14
|
+
} catch {
|
|
15
|
+
return false;
|
|
92
16
|
}
|
|
93
|
-
var errorMessages = "";
|
|
94
|
-
if (results.error.length > 0) {
|
|
95
|
-
errorMessages = "Failed to configure package managers:" + results.error.map((e) => e.packageManager + " - " + e.error).join(", ");
|
|
96
|
-
}
|
|
97
|
-
var successMessages = "";
|
|
98
|
-
if (results.success.length > 0) {
|
|
99
|
-
successMessages = "Package managers configured successfully:" + results.success.map((s) => s.packageManager).join(", ");
|
|
100
|
-
}
|
|
101
|
-
var success = true;
|
|
102
|
-
if (results.error.length > 0) {
|
|
103
|
-
success = false;
|
|
104
|
-
}
|
|
105
|
-
log("return message=" + errorMessages + successMessages);
|
|
106
|
-
return { success, message: errorMessages + " " + successMessages };
|
|
107
17
|
};
|
|
108
|
-
var
|
|
109
|
-
const failedSkills = [];
|
|
110
|
-
const jfroginstructionExists = await $`test -f ${directory}/.jfrog/instructions/JFROG-INTEGRATION-MANAGEMENT.md`.nothrow().quiet();
|
|
111
|
-
if (jfroginstructionExists.exitCode !== 0) {
|
|
112
|
-
log("JFrog integration management instructions not found, importing them locally!");
|
|
113
|
-
const result = await fetchAndSaveFile(`${INSTRUCTIONS_REGISTRY_URL}`, `${directory}/.jfrog/instructions/JFROG-INTEGRATION-MANAGEMENT.md`, log);
|
|
114
|
-
if (!result.success) {
|
|
115
|
-
log(`Failed to import JFrog integration management instructions for Opencode: ${result.error}`);
|
|
116
|
-
failedSkills.push("JFROG-INTEGRATION-MANAGEMENT");
|
|
117
|
-
}
|
|
118
|
-
log("JFrog integration management instructions imported!");
|
|
119
|
-
}
|
|
120
|
-
const response = await fetch(SKILLS_TO_INSTALL_URL);
|
|
121
|
-
if (!response.body) {
|
|
122
|
-
log(`Failed to fetch base skills list from ${SKILLS_TO_INSTALL_URL}, No response body from ${SKILLS_TO_INSTALL_URL}`);
|
|
123
|
-
return {
|
|
124
|
-
success: false,
|
|
125
|
-
failedSkills: [
|
|
126
|
-
`ALL Skills failed to fetch, No response body from ${SKILLS_TO_INSTALL_URL}, cannot install skills`
|
|
127
|
-
]
|
|
128
|
-
};
|
|
129
|
-
}
|
|
130
|
-
const skillsBody = await response.json();
|
|
131
|
-
if (!skillsBody.skills) {
|
|
132
|
-
log(`Failed to fetch base skills list from ${SKILLS_TO_INSTALL_URL}, No skills body from ${SKILLS_TO_INSTALL_URL}`);
|
|
133
|
-
return {
|
|
134
|
-
success: false,
|
|
135
|
-
failedSkills: [
|
|
136
|
-
`ALL Skills failed to fetch, No skills body from ${SKILLS_TO_INSTALL_URL}, cannot install skills`
|
|
137
|
-
]
|
|
138
|
-
};
|
|
139
|
-
}
|
|
140
|
-
const skillsToPull = skillsBody.skills.map((skill) => ({
|
|
141
|
-
name: skill.name,
|
|
142
|
-
version: skill.version
|
|
143
|
-
}));
|
|
144
|
-
log(`HOME: ${process.env.HOME}`);
|
|
145
|
-
const skillsDir = join(process.env.HOME || "~", ".config", "opencode", "skills");
|
|
146
|
-
if (!existsSync(skillsDir)) {
|
|
147
|
-
mkdirSync(skillsDir, { recursive: true });
|
|
148
|
-
log(`Skills directory created: ${skillsDir}`);
|
|
149
|
-
}
|
|
150
|
-
for (const skill of skillsToPull) {
|
|
151
|
-
const keepVersion = String(skill.version).trim();
|
|
152
|
-
const skillInstallDir = join(skillsDir, skill.name, keepVersion);
|
|
153
|
-
const skillExists = existsSync(skillInstallDir) && statSync(skillInstallDir).isDirectory();
|
|
154
|
-
if (!skillExists) {
|
|
155
|
-
log(`JFrog ${skill.name}-${keepVersion} skill not found, importing them locally!`);
|
|
156
|
-
const skillName = skill.name;
|
|
157
|
-
const skillVersion = keepVersion;
|
|
158
|
-
const skillZipDir = join(skillsDir, skillName, skillVersion);
|
|
159
|
-
const skillZipFile = join(skillZipDir, `${skillName}-${skillVersion}.zip`);
|
|
160
|
-
const result = await fetchAndSaveFile(`${SKILLS_REGISTRY_URL}/${skillName}/${skillVersion}/${skillName}-${skillVersion}.zip`, `${skillZipFile}`, log);
|
|
161
|
-
if (!result.success) {
|
|
162
|
-
log("Failed to import JFrog mcp skill: " + result.error);
|
|
163
|
-
failedSkills.push(skillName);
|
|
164
|
-
} else {
|
|
165
|
-
const unzipResult = await extractZip($, skillZipFile, skillName, skillVersion, skillZipDir, log);
|
|
166
|
-
if (!unzipResult.success) {
|
|
167
|
-
log(`Failed to extract ${skillName} skill: ${unzipResult.error}`);
|
|
168
|
-
failedSkills.push(skillName);
|
|
169
|
-
} else {
|
|
170
|
-
log(`${skillName} skill handling completed!`);
|
|
171
|
-
pruneNonManifestSkillVersions(skillsDir, skillName, keepVersion, log);
|
|
172
|
-
}
|
|
173
|
-
}
|
|
174
|
-
} else {
|
|
175
|
-
log(`JFrog ${skill.name}-${keepVersion} skill already present.`);
|
|
176
|
-
}
|
|
177
|
-
}
|
|
178
|
-
if (failedSkills.length > 0) {
|
|
179
|
-
return { success: false, failedSkills };
|
|
180
|
-
} else {
|
|
181
|
-
return { success: true };
|
|
182
|
-
}
|
|
183
|
-
};
|
|
184
|
-
var pruneNonManifestSkillVersions = (skillsDir, skillName, keepVersion, log) => {
|
|
185
|
-
const skillRoot = join(skillsDir, skillName);
|
|
186
|
-
if (!existsSync(skillRoot)) {
|
|
187
|
-
log(`No local version dirs for ${skillName} under ${skillRoot}`);
|
|
188
|
-
return;
|
|
189
|
-
}
|
|
190
|
-
let entries;
|
|
191
|
-
try {
|
|
192
|
-
entries = readdirSync(skillRoot);
|
|
193
|
-
} catch (e) {
|
|
194
|
-
log(`Could not list versions under ${skillRoot}: ${e}`);
|
|
195
|
-
return;
|
|
196
|
-
}
|
|
197
|
-
log(`Found version dirs for ${skillName}: ${entries.join(", ")} (latest version: ${keepVersion})`);
|
|
198
|
-
for (const olderVersion of entries) {
|
|
199
|
-
const versionPath = join(skillRoot, olderVersion);
|
|
200
|
-
let isDir = false;
|
|
201
|
-
try {
|
|
202
|
-
isDir = statSync(versionPath).isDirectory();
|
|
203
|
-
} catch {
|
|
204
|
-
continue;
|
|
205
|
-
}
|
|
206
|
-
if (!isDir) {
|
|
207
|
-
continue;
|
|
208
|
-
}
|
|
209
|
-
if (olderVersion === keepVersion) {
|
|
210
|
-
continue;
|
|
211
|
-
}
|
|
212
|
-
log(`Removing non-manifest version ${olderVersion} of ${skillName} (manifest: ${keepVersion})...`);
|
|
213
|
-
try {
|
|
214
|
-
rmSync(versionPath, { recursive: true, force: true });
|
|
215
|
-
log(`Removed ${skillName}/${olderVersion}`);
|
|
216
|
-
} catch (e) {
|
|
217
|
-
log(`Failed to remove ${skillName}/${olderVersion}: ${e}`);
|
|
218
|
-
}
|
|
219
|
-
}
|
|
220
|
-
};
|
|
221
|
-
var jfrogOpencodePlugin = async ({ client, $, directory }) => {
|
|
18
|
+
var jfrogOpencodePlugin = async ({ client }) => {
|
|
222
19
|
const logDir = dirname(LOG_FILE);
|
|
223
20
|
if (!existsSync(logDir)) {
|
|
224
21
|
mkdirSync(logDir, { recursive: true });
|
|
@@ -229,36 +26,31 @@ var jfrogOpencodePlugin = async ({ client, $, directory }) => {
|
|
|
229
26
|
`, "utf-8");
|
|
230
27
|
}
|
|
231
28
|
};
|
|
29
|
+
const toast = (message, variant) => {
|
|
30
|
+
client.tui.showToast({ body: { message, variant, duration: 1e4 } }).catch(() => {
|
|
31
|
+
return;
|
|
32
|
+
});
|
|
33
|
+
};
|
|
232
34
|
log("JfrogOpencodePlugin starting...");
|
|
233
|
-
|
|
234
|
-
log("pullSkillsResponse=" + JSON.stringify(pullSkillsResponse));
|
|
35
|
+
let nudgeShown = false;
|
|
235
36
|
return {
|
|
236
37
|
config: async (config) => {
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
38
|
+
const cfg = config;
|
|
39
|
+
cfg.skills = cfg.skills ?? {};
|
|
40
|
+
cfg.skills.paths = cfg.skills.paths ?? [];
|
|
41
|
+
if (!isNonEmptyDir(BUNDLED_SKILLS_DIR)) {
|
|
42
|
+
const message = `JFrog: bundled skills not found at ${BUNDLED_SKILLS_DIR}. ` + "The plugin package may be broken; reinstall @jfrog/opencode-jfrog-plugin.";
|
|
43
|
+
log("ERROR " + message);
|
|
44
|
+
toast(message, "error");
|
|
45
|
+
return;
|
|
46
|
+
}
|
|
47
|
+
if (!cfg.skills.paths.includes(BUNDLED_SKILLS_DIR)) {
|
|
48
|
+
cfg.skills.paths.push(BUNDLED_SKILLS_DIR);
|
|
241
49
|
}
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
const responses = [];
|
|
247
|
-
var pkgMngrResponse;
|
|
248
|
-
pkgMngrResponse = await setupPackageManagers(client, $, directory, sessionId, log);
|
|
249
|
-
if (pkgMngrResponse) {
|
|
250
|
-
responses.push(pkgMngrResponse);
|
|
251
|
-
}
|
|
252
|
-
log("pkgMngrResponse=" + pkgMngrResponse?.message);
|
|
253
|
-
await client.tui.showToast({
|
|
254
|
-
body: {
|
|
255
|
-
message: responses.filter((s) => !s.success).map((s) => s.message).join(`
|
|
256
|
-
|
|
257
|
-
`),
|
|
258
|
-
variant: "error",
|
|
259
|
-
duration: 1e4
|
|
260
|
-
}
|
|
261
|
-
});
|
|
50
|
+
log("config.skills.paths=" + JSON.stringify(cfg.skills.paths));
|
|
51
|
+
if (!nudgeShown) {
|
|
52
|
+
nudgeShown = true;
|
|
53
|
+
toast("JFrog: run `jf setup <pm>` to configure package managers against Artifactory.", "info");
|
|
262
54
|
}
|
|
263
55
|
}
|
|
264
56
|
};
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@jfrog/opencode-jfrog-plugin",
|
|
3
|
-
"version": "0.0.
|
|
3
|
+
"version": "0.0.4",
|
|
4
4
|
"description": "JFrog Plugin for seamless integration to Opencode",
|
|
5
5
|
"author": {
|
|
6
6
|
"name": "JFrog",
|
|
@@ -18,7 +18,8 @@
|
|
|
18
18
|
"jfrog",
|
|
19
19
|
"artifactory",
|
|
20
20
|
"opencode",
|
|
21
|
-
"plugin"
|
|
21
|
+
"plugin",
|
|
22
|
+
"skills"
|
|
22
23
|
],
|
|
23
24
|
"repository": {
|
|
24
25
|
"type": "git",
|
|
@@ -30,12 +31,14 @@
|
|
|
30
31
|
},
|
|
31
32
|
"files": [
|
|
32
33
|
"dist",
|
|
33
|
-
"
|
|
34
|
+
"skills",
|
|
35
|
+
"sync-skills-vendor.json"
|
|
34
36
|
],
|
|
35
37
|
"main": "dist/index.js",
|
|
36
38
|
"types": "dist/index.d.ts",
|
|
37
39
|
"devDependencies": {
|
|
38
40
|
"@eslint/js": "^9.39.1",
|
|
41
|
+
"@opencode-ai/plugin": "^1.4.6",
|
|
39
42
|
"@types/node": "^20.11.5",
|
|
40
43
|
"@typescript-eslint/eslint-plugin": "8.47.0",
|
|
41
44
|
"@typescript-eslint/parser": "8.47.0",
|
|
@@ -46,8 +49,5 @@
|
|
|
46
49
|
"prettier": "^3.2.4",
|
|
47
50
|
"typescript-eslint": "^8.47.0",
|
|
48
51
|
"vitest": "^3.2.4"
|
|
49
|
-
},
|
|
50
|
-
"dependencies": {
|
|
51
|
-
"@opencode-ai/plugin": "^1.4.6"
|
|
52
52
|
}
|
|
53
53
|
}
|