skills-layer 0.1.8 → 0.2.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 +2 -2
- package/THIRD_PARTY_NOTICES.md +718 -0
- package/package.json +6 -10
- package/public/skills-layer-public.mjs +961 -270
- package/scripts/skills-layer.mjs +48 -7
- package/.github/dependabot.yml +0 -7
- package/.github/workflows/ci.yml +0 -22
- package/.github/workflows/publish.yml +0 -24
- package/CODE_OF_CONDUCT.md +0 -18
- package/CONTRIBUTING.md +0 -24
- package/tests/public-cli-package.test.mjs +0 -311
package/scripts/skills-layer.mjs
CHANGED
|
@@ -1,15 +1,56 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
|
|
3
|
-
import { executePublicCli } from "../public/skills-layer-public.mjs";
|
|
3
|
+
import { createCliProgress, createRevealSkipController, executePublicCli, prepareCliPresentation, writeCliResult } from "../public/skills-layer-public.mjs";
|
|
4
4
|
|
|
5
|
-
const
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
process.stdout.
|
|
5
|
+
const args = process.argv.slice(2);
|
|
6
|
+
const capabilities = {
|
|
7
|
+
stdinIsTty: Boolean(process.stdin.isTTY),
|
|
8
|
+
stdoutIsTty: Boolean(process.stdout.isTTY),
|
|
9
|
+
stderrIsTty: Boolean(process.stderr.isTTY)
|
|
10
|
+
};
|
|
11
|
+
let presentation;
|
|
12
|
+
try {
|
|
13
|
+
presentation = prepareCliPresentation(args, process.env, capabilities);
|
|
14
|
+
} catch {
|
|
15
|
+
const outputFormatIndex = args.indexOf("--output-format");
|
|
16
|
+
const inlineOutputFormat = args.find((argument) => argument.startsWith("--output-format="))?.slice("--output-format=".length);
|
|
17
|
+
const requestedOutputFormat = outputFormatIndex >= 0 ? args[outputFormatIndex + 1] : inlineOutputFormat;
|
|
18
|
+
const kind = requestedOutputFormat === "stream-json" ? "json-stream" : args.includes("--json") || requestedOutputFormat === "json" ? "json-static" : "human-static";
|
|
19
|
+
presentation = { args, mode: { kind, color: false, motion: false, interactive: false, unicode: true, screenReader: false, classic: true, detail: "standard" } };
|
|
20
|
+
}
|
|
21
|
+
const human = presentation.mode.kind === "human-interactive" || presentation.mode.kind === "human-static";
|
|
22
|
+
const commandName = presentation.args?.find((argument) => !argument.startsWith("-")) ?? "skills-layer";
|
|
23
|
+
const lightweight = args.length === 0 || args.includes("--help") || args.includes("-h") || args.includes("--version") || args.includes("-v") || commandName === "help" || commandName === "version";
|
|
24
|
+
const progress = lightweight
|
|
25
|
+
? { info() {}, pause() {}, resume() {}, finish() {} }
|
|
26
|
+
: createCliProgress({ mode: presentation.mode, commandName, stream: process.stderr });
|
|
27
|
+
if (human && !lightweight) {
|
|
28
|
+
progress.info(`Running ${commandName}…`);
|
|
9
29
|
}
|
|
30
|
+
const result = await executePublicCli(args, {
|
|
31
|
+
stdinIsTty: capabilities.stdinIsTty,
|
|
32
|
+
stdoutIsTty: capabilities.stdoutIsTty,
|
|
33
|
+
stderrIsTty: capabilities.stderrIsTty,
|
|
34
|
+
...(presentation.mode.kind === "json-stream" ? {
|
|
35
|
+
emitCliEvent: (event) => process.stdout.write(`${JSON.stringify(event)}\n`)
|
|
36
|
+
} : {}),
|
|
37
|
+
...(human ? { emitInfo: progress.info } : {}),
|
|
38
|
+
...(human ? { onPromptStart: progress.pause, onPromptEnd: progress.resume } : {})
|
|
39
|
+
});
|
|
40
|
+
progress.finish(result);
|
|
10
41
|
|
|
11
|
-
|
|
12
|
-
|
|
42
|
+
const revealSkip = createRevealSkipController(process.stdin);
|
|
43
|
+
try {
|
|
44
|
+
await writeCliResult(result, {
|
|
45
|
+
mode: presentation.mode,
|
|
46
|
+
writeStdout: (value) => process.stdout.write(value),
|
|
47
|
+
writeStderr: (value) => process.stderr.write(value),
|
|
48
|
+
sleep: (milliseconds) => new Promise((resolve) => setTimeout(resolve, milliseconds)),
|
|
49
|
+
shouldSkip: revealSkip.shouldSkip,
|
|
50
|
+
width: process.stdout.columns
|
|
51
|
+
});
|
|
52
|
+
} finally {
|
|
53
|
+
revealSkip.dispose();
|
|
13
54
|
}
|
|
14
55
|
|
|
15
56
|
process.exitCode = result.exitCode;
|
package/.github/dependabot.yml
DELETED
package/.github/workflows/ci.yml
DELETED
|
@@ -1,22 +0,0 @@
|
|
|
1
|
-
name: CLI package CI
|
|
2
|
-
|
|
3
|
-
on:
|
|
4
|
-
pull_request:
|
|
5
|
-
push:
|
|
6
|
-
branches:
|
|
7
|
-
- main
|
|
8
|
-
|
|
9
|
-
permissions:
|
|
10
|
-
contents: read
|
|
11
|
-
|
|
12
|
-
jobs:
|
|
13
|
-
verify:
|
|
14
|
-
runs-on: ubuntu-latest
|
|
15
|
-
steps:
|
|
16
|
-
- uses: actions/checkout@v6
|
|
17
|
-
- uses: actions/setup-node@v6
|
|
18
|
-
with:
|
|
19
|
-
node-version: "24"
|
|
20
|
-
package-manager-cache: false
|
|
21
|
-
- run: npm run typecheck
|
|
22
|
-
- run: npm run test:coverage
|
|
@@ -1,24 +0,0 @@
|
|
|
1
|
-
name: Publish skills-layer to npm
|
|
2
|
-
|
|
3
|
-
on:
|
|
4
|
-
push:
|
|
5
|
-
tags:
|
|
6
|
-
- "v*"
|
|
7
|
-
|
|
8
|
-
permissions:
|
|
9
|
-
contents: read
|
|
10
|
-
id-token: write
|
|
11
|
-
|
|
12
|
-
jobs:
|
|
13
|
-
publish:
|
|
14
|
-
runs-on: ubuntu-latest
|
|
15
|
-
steps:
|
|
16
|
-
- uses: actions/checkout@v6
|
|
17
|
-
- uses: actions/setup-node@v6
|
|
18
|
-
with:
|
|
19
|
-
node-version: "24"
|
|
20
|
-
registry-url: "https://registry.npmjs.org"
|
|
21
|
-
package-manager-cache: false
|
|
22
|
-
- run: npm run typecheck
|
|
23
|
-
- run: npm run test:coverage
|
|
24
|
-
- run: npm publish --access public --provenance
|
package/CODE_OF_CONDUCT.md
DELETED
|
@@ -1,18 +0,0 @@
|
|
|
1
|
-
# Code Of Conduct
|
|
2
|
-
|
|
3
|
-
We want Skills Layer CLI to be a respectful, practical, and welcoming project for users and contributors.
|
|
4
|
-
|
|
5
|
-
## Expected Behavior
|
|
6
|
-
|
|
7
|
-
- Be respectful and constructive.
|
|
8
|
-
- Assume good intent while being precise about risks and tradeoffs.
|
|
9
|
-
- Keep security reports private until they are resolved or responsibly disclosed.
|
|
10
|
-
- Avoid harassment, insults, discrimination, or repeated disruptive behavior.
|
|
11
|
-
|
|
12
|
-
## Scope
|
|
13
|
-
|
|
14
|
-
This applies to repository issues, pull requests, discussions, and related project communication.
|
|
15
|
-
|
|
16
|
-
## Enforcement
|
|
17
|
-
|
|
18
|
-
Maintainers may edit, hide, or remove content and may restrict participation when behavior harms users, contributors, or project safety.
|
package/CONTRIBUTING.md
DELETED
|
@@ -1,24 +0,0 @@
|
|
|
1
|
-
# Contributing
|
|
2
|
-
|
|
3
|
-
Thanks for helping improve Skills Layer CLI.
|
|
4
|
-
|
|
5
|
-
This public repository is intentionally limited to the CLI, MCP setup, local agent integration, packaging, docs, and package-level tests. Do not submit backend service code, Cloud admin tooling, Guardian scoring internals, credentials, or private release material here.
|
|
6
|
-
|
|
7
|
-
## Local Checks
|
|
8
|
-
|
|
9
|
-
Run these from the repository root before opening a pull request:
|
|
10
|
-
|
|
11
|
-
```bash
|
|
12
|
-
npm run typecheck
|
|
13
|
-
npm run test:coverage
|
|
14
|
-
```
|
|
15
|
-
|
|
16
|
-
The package has no runtime dependencies and no npm lifecycle scripts. Please keep it that way unless a public CLI feature has a clear need.
|
|
17
|
-
|
|
18
|
-
## Pull Request Checklist
|
|
19
|
-
|
|
20
|
-
- Keep user-facing CLI output readable and agent-friendly.
|
|
21
|
-
- Preserve predictable `--json` behavior for agent workflows.
|
|
22
|
-
- Avoid leaking internal service details or private operational commands.
|
|
23
|
-
- Update README/help text when behavior changes.
|
|
24
|
-
- Add focused tests for changed behavior.
|
|
@@ -1,311 +0,0 @@
|
|
|
1
|
-
import { execFile } from "node:child_process";
|
|
2
|
-
import test from "node:test";
|
|
3
|
-
import assert from "node:assert/strict";
|
|
4
|
-
import { mkdtemp, readFile, writeFile } from "node:fs/promises";
|
|
5
|
-
import { tmpdir } from "node:os";
|
|
6
|
-
import { join } from "node:path";
|
|
7
|
-
import { fileURLToPath } from "node:url";
|
|
8
|
-
|
|
9
|
-
const packageUrl = new URL("../package.json", import.meta.url);
|
|
10
|
-
const runtimeUrl = new URL("../public/skills-layer-public.mjs", import.meta.url);
|
|
11
|
-
const readmeUrl = new URL("../README.md", import.meta.url);
|
|
12
|
-
const licenseUrl = new URL("../LICENSE", import.meta.url);
|
|
13
|
-
const wrapperUrl = new URL("../scripts/skills-layer.mjs", import.meta.url);
|
|
14
|
-
const securityUrl = new URL("../SECURITY.md", import.meta.url);
|
|
15
|
-
const contributingUrl = new URL("../CONTRIBUTING.md", import.meta.url);
|
|
16
|
-
const codeOfConductUrl = new URL("../CODE_OF_CONDUCT.md", import.meta.url);
|
|
17
|
-
const dependabotUrl = new URL("../.github/dependabot.yml", import.meta.url);
|
|
18
|
-
const ciWorkflowUrl = new URL("../.github/workflows/ci.yml", import.meta.url);
|
|
19
|
-
const trustedPublishWorkflowUrl = new URL("../.github/workflows/publish.yml", import.meta.url);
|
|
20
|
-
const binPath = fileURLToPath(new URL("../bin/skills-layer.js", import.meta.url));
|
|
21
|
-
|
|
22
|
-
async function loadRuntime() {
|
|
23
|
-
return await import(runtimeUrl.href);
|
|
24
|
-
}
|
|
25
|
-
|
|
26
|
-
async function makeHome() {
|
|
27
|
-
return await mkdtemp(join(tmpdir(), "skills-layer-cli-package-home-"));
|
|
28
|
-
}
|
|
29
|
-
|
|
30
|
-
function packageEnv(home) {
|
|
31
|
-
const env = {
|
|
32
|
-
...process.env,
|
|
33
|
-
HOME: home,
|
|
34
|
-
SKILLS_LAYER_NO_BANNER: "1"
|
|
35
|
-
};
|
|
36
|
-
delete env.SKILLS_LAYER_API_KEY;
|
|
37
|
-
delete env.SKILLS_LAYER_BASE_URL;
|
|
38
|
-
delete env.SKILLS_LAYER_MCP_TOKEN;
|
|
39
|
-
return env;
|
|
40
|
-
}
|
|
41
|
-
|
|
42
|
-
async function runPublic(args, options = {}) {
|
|
43
|
-
const runtime = await loadRuntime();
|
|
44
|
-
return await runtime.executePublicCli(args, {
|
|
45
|
-
env: packageEnv(options.home ?? await makeHome()),
|
|
46
|
-
cwd: options.cwd,
|
|
47
|
-
stdoutIsTty: false,
|
|
48
|
-
fetch: options.fetch ?? (async () => { throw new Error("Unexpected network call from package contract test."); })
|
|
49
|
-
});
|
|
50
|
-
}
|
|
51
|
-
|
|
52
|
-
async function runBin(args) {
|
|
53
|
-
const home = await makeHome();
|
|
54
|
-
return await new Promise((resolve) => {
|
|
55
|
-
execFile(process.execPath, [binPath, ...args], { env: packageEnv(home) }, (error, stdout, stderr) => {
|
|
56
|
-
resolve({
|
|
57
|
-
exitCode: typeof error?.code === "number" ? error.code : 0,
|
|
58
|
-
stdout,
|
|
59
|
-
stderr
|
|
60
|
-
});
|
|
61
|
-
});
|
|
62
|
-
});
|
|
63
|
-
}
|
|
64
|
-
|
|
65
|
-
function parseJsonOutput(result) {
|
|
66
|
-
assert.equal(result.stderr, "");
|
|
67
|
-
return JSON.parse(result.stdout);
|
|
68
|
-
}
|
|
69
|
-
|
|
70
|
-
function parseJsonError(result) {
|
|
71
|
-
assert.equal(result.stdout, "");
|
|
72
|
-
return JSON.parse(result.stderr);
|
|
73
|
-
}
|
|
74
|
-
|
|
75
|
-
function jsonResponse(body, status = 200) {
|
|
76
|
-
return new Response(JSON.stringify(body), {
|
|
77
|
-
status,
|
|
78
|
-
headers: { "content-type": "application/json" }
|
|
79
|
-
});
|
|
80
|
-
}
|
|
81
|
-
|
|
82
|
-
test("public package metadata keeps the npm tree narrow and non-lifecycle", async () => {
|
|
83
|
-
const packageJson = JSON.parse(await readFile(packageUrl, "utf8"));
|
|
84
|
-
assert.equal(packageJson.name, "skills-layer");
|
|
85
|
-
assert.equal(packageJson.private, false);
|
|
86
|
-
assert.equal(packageJson.type, "module");
|
|
87
|
-
assert.equal(packageJson.bin["skills-layer"], "bin/skills-layer.js");
|
|
88
|
-
assert.equal(packageJson.dependencies, undefined);
|
|
89
|
-
assert.equal(packageJson.devDependencies, undefined);
|
|
90
|
-
assert.equal(packageJson.license, "Apache-2.0");
|
|
91
|
-
assert.deepEqual(packageJson.repository, {
|
|
92
|
-
type: "git",
|
|
93
|
-
url: "git+https://github.com/ankurdhoom/skills-layer.git"
|
|
94
|
-
});
|
|
95
|
-
assert.deepEqual(packageJson.bugs, { url: "https://github.com/ankurdhoom/skills-layer/issues" });
|
|
96
|
-
assert.deepEqual(packageJson.files, [
|
|
97
|
-
"CODE_OF_CONDUCT.md",
|
|
98
|
-
"CONTRIBUTING.md",
|
|
99
|
-
"SECURITY.md",
|
|
100
|
-
".github/dependabot.yml",
|
|
101
|
-
".github/workflows/*.yml",
|
|
102
|
-
"bin/skills-layer.js",
|
|
103
|
-
"public/skills-layer-public.mjs",
|
|
104
|
-
"scripts/skills-layer.mjs",
|
|
105
|
-
"docs/assets/*.svg",
|
|
106
|
-
"tests/public-cli-package.test.mjs"
|
|
107
|
-
]);
|
|
108
|
-
assert.deepEqual(Object.keys(packageJson.scripts).sort(), ["test", "test:coverage", "typecheck"]);
|
|
109
|
-
for (const lifecycleScript of ["preinstall", "install", "postinstall", "prepare", "prepack", "postpack", "prepublishOnly"]) {
|
|
110
|
-
assert.equal(packageJson.scripts[lifecycleScript], undefined);
|
|
111
|
-
}
|
|
112
|
-
|
|
113
|
-
const workflow = await readFile(trustedPublishWorkflowUrl, "utf8");
|
|
114
|
-
assert.match(workflow, /name: Publish skills-layer to npm/u);
|
|
115
|
-
assert.match(workflow, /id-token: write/u);
|
|
116
|
-
assert.match(workflow, /npm publish --access public --provenance/u);
|
|
117
|
-
assert.doesNotMatch(workflow, /NPM_TOKEN/u);
|
|
118
|
-
|
|
119
|
-
const ciWorkflow = await readFile(ciWorkflowUrl, "utf8");
|
|
120
|
-
assert.match(ciWorkflow, /name: CLI package CI/u);
|
|
121
|
-
assert.match(ciWorkflow, /npm run typecheck/u);
|
|
122
|
-
assert.match(ciWorkflow, /npm run test:coverage/u);
|
|
123
|
-
assert.doesNotMatch(ciWorkflow, /NPM_TOKEN/u);
|
|
124
|
-
|
|
125
|
-
const dependabot = await readFile(dependabotUrl, "utf8");
|
|
126
|
-
assert.match(dependabot, /package-ecosystem: npm/u);
|
|
127
|
-
|
|
128
|
-
const [security, contributing, codeOfConduct] = await Promise.all([
|
|
129
|
-
readFile(securityUrl, "utf8"),
|
|
130
|
-
readFile(contributingUrl, "utf8"),
|
|
131
|
-
readFile(codeOfConductUrl, "utf8")
|
|
132
|
-
]);
|
|
133
|
-
assert.match(security, /Please do not open public issues for active vulnerabilities/u);
|
|
134
|
-
assert.match(security, /hello@skills-layer\.com/u);
|
|
135
|
-
assert.match(contributing, /Do not submit backend service code/u);
|
|
136
|
-
assert.match(codeOfConduct, /Expected Behavior/u);
|
|
137
|
-
});
|
|
138
|
-
|
|
139
|
-
test("public package text stays limited to public CLI surface", async () => {
|
|
140
|
-
const [packageText, readme, license, runtime, wrapper] = await Promise.all([
|
|
141
|
-
readFile(packageUrl, "utf8"),
|
|
142
|
-
readFile(readmeUrl, "utf8"),
|
|
143
|
-
readFile(licenseUrl, "utf8"),
|
|
144
|
-
readFile(runtimeUrl, "utf8"),
|
|
145
|
-
readFile(wrapperUrl, "utf8")
|
|
146
|
-
]);
|
|
147
|
-
const combined = `${packageText}\n${readme}\n${license}\n${runtime}\n${wrapper}`;
|
|
148
|
-
assert.match(combined, /Skills Layer/u);
|
|
149
|
-
assert.match(combined, /skills-layer/u);
|
|
150
|
-
assert.match(readme, /open-source CLI, MCP setup, and local agent integration layer/u);
|
|
151
|
-
assert.match(readme, /hosted backend code, marketplace services, Guardian scoring internals, and service operations remain proprietary/u);
|
|
152
|
-
assert.match(readme, /actions\/workflows\/ci\.yml\/badge\.svg/u);
|
|
153
|
-
assert.match(readme, /https:\/\/unpkg\.com\/skills-layer\/docs\/assets\/skills-layer-logo\.svg/u);
|
|
154
|
-
assert.match(readme, /https:\/\/unpkg\.com\/skills-layer\/docs\/assets\/skills-layer-hero\.svg/u);
|
|
155
|
-
assert.match(readme, /https:\/\/unpkg\.com\/skills-layer\/docs\/assets\/skills-layer-flow\.svg/u);
|
|
156
|
-
assert.match(readme, /https:\/\/unpkg\.com\/skills-layer\/docs\/assets\/skills-layer-marketplace-flow\.svg/u);
|
|
157
|
-
assert.doesNotMatch(readme, /src="docs\/assets\//u);
|
|
158
|
-
for (const forbidden of [
|
|
159
|
-
new RegExp(["", "api", "v1", "admin"].join("/"), "u"),
|
|
160
|
-
new RegExp(["x", "skills", "layer", "admin", "key"].join("-"), "u"),
|
|
161
|
-
new RegExp(["SKILLS", "LAYER", "ADMIN", "API", "KEY"].join("_"), "u"),
|
|
162
|
-
new RegExp(["--", "admin", "api", "key"].join("-"), "u"),
|
|
163
|
-
new RegExp(["github.com", "openai", "openskills"].join("/"), "u")
|
|
164
|
-
]) {
|
|
165
|
-
assert.doesNotMatch(combined, forbidden);
|
|
166
|
-
}
|
|
167
|
-
});
|
|
168
|
-
|
|
169
|
-
test("public bin wrapper and runtime expose deterministic package smoke commands", async () => {
|
|
170
|
-
const packageJson = JSON.parse(await readFile(packageUrl, "utf8"));
|
|
171
|
-
const expectedVersionLine = `skills-layer ${packageJson.version}`;
|
|
172
|
-
const runtime = await loadRuntime();
|
|
173
|
-
const binVersion = await runBin(["--version"]);
|
|
174
|
-
assert.equal(binVersion.exitCode, 0);
|
|
175
|
-
assert.equal(binVersion.stderr, "");
|
|
176
|
-
assert.equal(binVersion.stdout.trim(), expectedVersionLine);
|
|
177
|
-
|
|
178
|
-
const version = await runtime.executePublicCli(["--version"]);
|
|
179
|
-
assert.equal(version.exitCode, 0);
|
|
180
|
-
assert.equal(version.stdout.trim(), expectedVersionLine);
|
|
181
|
-
|
|
182
|
-
const jsonVersion = await runPublic(["version", "--json"]);
|
|
183
|
-
assert.equal(jsonVersion.exitCode, 0);
|
|
184
|
-
assert.equal(parseJsonOutput(jsonVersion).data.version, packageJson.version);
|
|
185
|
-
|
|
186
|
-
const loginHelp = await runtime.executePublicCli(["login", "--help"]);
|
|
187
|
-
assert.equal(loginHelp.exitCode, 0);
|
|
188
|
-
assert.match(loginHelp.stdout, /Skills Layer login help/u);
|
|
189
|
-
assert.match(loginHelp.stdout, /skills-layer mcp setup/u);
|
|
190
|
-
|
|
191
|
-
const fullHelp = await runtime.executePublicCli(["help", "full"]);
|
|
192
|
-
assert.equal(fullHelp.exitCode, 0);
|
|
193
|
-
assert.match(fullHelp.stdout, /Start here:/u);
|
|
194
|
-
assert.match(fullHelp.stdout, /skills-layer help full/u);
|
|
195
|
-
});
|
|
196
|
-
|
|
197
|
-
test("public runtime persists backend preference without network access", async () => {
|
|
198
|
-
const home = await makeHome();
|
|
199
|
-
const update = await runPublic(["backend", "--base-url", "https://staging.findfigg.co.uk", "--json"], { home });
|
|
200
|
-
assert.equal(update.exitCode, 0);
|
|
201
|
-
assert.equal(parseJsonOutput(update).data.selectedBaseUrl, "https://staging.findfigg.co.uk");
|
|
202
|
-
|
|
203
|
-
const preference = JSON.parse(await readFile(join(home, ".skills-layer", "backend-preference.json"), "utf8"));
|
|
204
|
-
assert.equal(preference.preferredBaseUrl, "https://staging.findfigg.co.uk");
|
|
205
|
-
|
|
206
|
-
const status = await runPublic(["backend", "status", "--json"], { home });
|
|
207
|
-
assert.equal(status.exitCode, 0);
|
|
208
|
-
const statusPayload = parseJsonOutput(status);
|
|
209
|
-
assert.equal(statusPayload.data.selectedBaseUrl, "https://staging.findfigg.co.uk");
|
|
210
|
-
assert.equal(statusPayload.data.loggedIn, false);
|
|
211
|
-
});
|
|
212
|
-
|
|
213
|
-
test("public runtime posts buyer eval summary envelopes with reviews", async () => {
|
|
214
|
-
const home = await makeHome();
|
|
215
|
-
const cwd = await mkdtemp(join(tmpdir(), "skills-layer-cli-package-work-"));
|
|
216
|
-
const summaryPath = join(cwd, "buyer-eval-summary.json");
|
|
217
|
-
const artifactDigest = `sha256:${"a".repeat(64)}`;
|
|
218
|
-
await writeFile(summaryPath, JSON.stringify({
|
|
219
|
-
schemaVersion: "skills-layer.eval.summary.v1",
|
|
220
|
-
status: "passed",
|
|
221
|
-
skillSlug: "project-planner",
|
|
222
|
-
version: "1.0.0",
|
|
223
|
-
artifactDigest,
|
|
224
|
-
evalPackDigest: `sha256:${"b".repeat(64)}`,
|
|
225
|
-
generatedAt: "2026-07-03T00:00:00.000Z",
|
|
226
|
-
counts: {
|
|
227
|
-
cases: 1,
|
|
228
|
-
assertions: 2,
|
|
229
|
-
passed: 2,
|
|
230
|
-
failed: 0,
|
|
231
|
-
skipped: 0
|
|
232
|
-
},
|
|
233
|
-
redaction: {
|
|
234
|
-
mode: "content_free",
|
|
235
|
-
rawContentIncluded: false
|
|
236
|
-
}
|
|
237
|
-
}), "utf8");
|
|
238
|
-
|
|
239
|
-
const fetch = async (input, init = {}) => {
|
|
240
|
-
const url = String(input);
|
|
241
|
-
if (url === "https://api.skills-layer.local/api/v1/me") {
|
|
242
|
-
assert.equal(init.headers.authorization, "Bearer package-test-key");
|
|
243
|
-
return jsonResponse({
|
|
244
|
-
userId: "user_package",
|
|
245
|
-
email: "package@example.com",
|
|
246
|
-
role: "buyer",
|
|
247
|
-
roles: ["buyer"]
|
|
248
|
-
});
|
|
249
|
-
}
|
|
250
|
-
if (url === "https://api.skills-layer.local/api/v1/capabilities") {
|
|
251
|
-
assert.equal(init.headers.authorization, "Bearer package-test-key");
|
|
252
|
-
return jsonResponse({ edition: "saas", capabilities: ["marketplace"] });
|
|
253
|
-
}
|
|
254
|
-
if (url === "https://api.skills-layer.local/api/v1/me/library/project-planner/review") {
|
|
255
|
-
assert.equal(init.method, "POST");
|
|
256
|
-
assert.equal(init.headers.authorization, "Bearer package-test-key");
|
|
257
|
-
const body = JSON.parse(String(init.body));
|
|
258
|
-
assert.deepEqual(body, {
|
|
259
|
-
rating: 5,
|
|
260
|
-
feedback: "Useful",
|
|
261
|
-
evalSummary: {
|
|
262
|
-
schemaVersion: "skills-layer.eval.summary.v1",
|
|
263
|
-
status: "passed",
|
|
264
|
-
skillSlug: "project-planner",
|
|
265
|
-
version: "1.0.0",
|
|
266
|
-
artifactDigest,
|
|
267
|
-
evalPackDigest: `sha256:${"b".repeat(64)}`,
|
|
268
|
-
generatedAt: "2026-07-03T00:00:00.000Z",
|
|
269
|
-
counts: {
|
|
270
|
-
cases: 1,
|
|
271
|
-
assertions: 2,
|
|
272
|
-
passed: 2,
|
|
273
|
-
failed: 0,
|
|
274
|
-
skipped: 0
|
|
275
|
-
},
|
|
276
|
-
redaction: {
|
|
277
|
-
mode: "content_free",
|
|
278
|
-
rawContentIncluded: false
|
|
279
|
-
}
|
|
280
|
-
}
|
|
281
|
-
});
|
|
282
|
-
return jsonResponse({ status: "reviewed", reviewId: "review_package", buyerEval: { status: "recorded" } });
|
|
283
|
-
}
|
|
284
|
-
throw new Error(`Unexpected package contract request: ${url}`);
|
|
285
|
-
};
|
|
286
|
-
|
|
287
|
-
const login = await runPublic(["login", "--api-key", "package-test-key", "--base-url", "https://api.skills-layer.local", "--no-mcp", "--json"], { home, cwd, fetch });
|
|
288
|
-
assert.equal(login.exitCode, 0);
|
|
289
|
-
assert.equal(parseJsonOutput(login).data.email, "package@example.com");
|
|
290
|
-
|
|
291
|
-
const review = await runPublic(["add-review", "project-planner", "--rating", "5", "--feedback", "Useful", "--eval-summary", summaryPath, "--json"], { home, cwd, fetch });
|
|
292
|
-
assert.equal(review.exitCode, 0);
|
|
293
|
-
assert.equal(parseJsonOutput(review).data.buyerEval.status, "recorded");
|
|
294
|
-
});
|
|
295
|
-
|
|
296
|
-
test("public runtime fails closed for internal and login-required commands", async () => {
|
|
297
|
-
const admin = await runPublic(["admin", "status", "--json"]);
|
|
298
|
-
assert.equal(admin.exitCode, 1);
|
|
299
|
-
const adminPayload = parseJsonError(admin);
|
|
300
|
-
assert.equal(adminPayload.status, "error");
|
|
301
|
-
assert.match(adminPayload.summary, /does not include internal commands/u);
|
|
302
|
-
|
|
303
|
-
const whoami = await runPublic(["whoami", "--json"]);
|
|
304
|
-
assert.equal(whoami.exitCode, 1);
|
|
305
|
-
const whoamiPayload = parseJsonError(whoami);
|
|
306
|
-
assert.equal(whoamiPayload.status, "login_required");
|
|
307
|
-
assert.equal(whoamiPayload.reasonCode, "login_required");
|
|
308
|
-
assert.equal(whoamiPayload.nextAction, "login");
|
|
309
|
-
assert.match(whoamiPayload.nextStep, /skills-layer login/u);
|
|
310
|
-
assert.doesNotMatch(whoamiPayload.summary, /--api-key/u);
|
|
311
|
-
});
|