@hasna/skills 0.2.0 → 0.3.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 +100 -36
- package/bin/index.js +2151 -650
- package/bin/mcp.js +1787 -625
- package/bin/migrate.js +5 -0
- package/bin/server.js +365 -198
- package/bin/worker.js +346 -179
- package/dist/cli/cli.test-utils.d.ts +14 -0
- package/dist/cli/commands/publish.d.ts +14 -0
- package/dist/index.d.ts +1 -0
- package/dist/index.js +1100 -152
- package/dist/lib/api-url.d.ts +32 -23
- package/dist/lib/auth-store.d.ts +111 -42
- package/dist/lib/config.d.ts +19 -21
- package/dist/lib/feedback.d.ts +8 -3
- package/dist/lib/fleet-credentials.d.ts +246 -0
- package/dist/lib/portable-skills-files.d.ts +9 -0
- package/dist/lib/portable-skills.d.ts +2 -2
- package/dist/lib/remote-client.d.ts +20 -10
- package/dist/lib/remote-registry.d.ts +31 -11
- package/dist/lib/run-routing.d.ts +6 -4
- package/dist/lib/vendor-host-policy.d.ts +31 -0
- package/dist/sdk/index.d.ts +6 -0
- package/dist/sdk/index.js +1738 -572
- package/dist/server/artifact-storage.d.ts +11 -0
- package/dist/server/skills-api.d.ts +13 -2
- package/dist/storage.js +9 -14
- package/package.json +3 -1
|
@@ -113,6 +113,17 @@ export declare class ArtifactStorage {
|
|
|
113
113
|
* <prefix>/skills/<org>/<slug>/<version>/manifest.json
|
|
114
114
|
* The content-addressed object under bundles/ stays the read path (dedupe); these keys
|
|
115
115
|
* are the durable, browsable history and are never deleted by orphan collection.
|
|
116
|
+
*
|
|
117
|
+
* RETENTION (hasna/apps#1671): these objects accumulate without bound by design — every
|
|
118
|
+
* version keeps a full copy — so the bucket lifecycle is the retention story, not the
|
|
119
|
+
* store. The intended rule (operated in the bucket's lifecycle config, outside this
|
|
120
|
+
* repository): expire `<prefix>/skills/` objects older than N days per the slot policy,
|
|
121
|
+
* while `<prefix>/bundles/` (content-addressed, deduped across versions and slugs) is
|
|
122
|
+
* NEVER expired by lifecycle — only the reference-guarded delete path removes those,
|
|
123
|
+
* because one object can serve many versions. A busy org that pushes thousands of
|
|
124
|
+
* versions should pair the lifecycle rule with a documented cap on retained versions
|
|
125
|
+
* per slug (e.g. the newest K, pruned by a maintenance job that deletes the version row
|
|
126
|
+
* and its two objects together); row deletion alone would strand the objects.
|
|
116
127
|
* Returns the placement recorded on the version row; in db mode nothing is written.
|
|
117
128
|
*/
|
|
118
129
|
putVersionObjects(orgId: string, slug: string, version: string, bytes: OwnedBytes, manifest: Record<string, unknown>, contentType?: string): Promise<{
|
|
@@ -22,7 +22,15 @@ export declare class SkillRequestError extends Error {
|
|
|
22
22
|
}
|
|
23
23
|
/** SkillMeta shape for a published row, so a client can treat both kinds alike. */
|
|
24
24
|
export declare function publishedSkillMeta(record: ServerSkillRecord): SkillMeta;
|
|
25
|
-
|
|
25
|
+
/**
|
|
26
|
+
* SkillMeta shape for a published row, plus a payload-level "alreadyPublished" flag:
|
|
27
|
+
* true when the accepted publish found the version already recorded with the same
|
|
28
|
+
* digest (an idempotent re-push), so the CLI can distinguish "published as X" from
|
|
29
|
+
* "already published as X" (hasna/apps#1671).
|
|
30
|
+
*/
|
|
31
|
+
export declare function publishedPayload(record: ServerSkillRecord, opts?: {
|
|
32
|
+
alreadyPublished?: boolean;
|
|
33
|
+
}): Record<string, unknown>;
|
|
26
34
|
/**
|
|
27
35
|
* The HTTP ETag for a published row: the revision id, quoted exactly as RFC 9110 wants.
|
|
28
36
|
* The quotes are load-bearing — a client that echoes the whole header value back as
|
|
@@ -143,7 +151,10 @@ interface ParsedPublish {
|
|
|
143
151
|
* measured.
|
|
144
152
|
*/
|
|
145
153
|
export declare function parsePublishRequest(request: Request, config: SkillsServerConfig): Promise<ParsedPublish>;
|
|
146
|
-
export declare function storePublishedSkill(store: SkillsProductStore, artifactStorage: ArtifactStorage, principal: ApiPrincipal, parsed: ParsedPublish, expectedRevisionId?: string): Promise<
|
|
154
|
+
export declare function storePublishedSkill(store: SkillsProductStore, artifactStorage: ArtifactStorage, principal: ApiPrincipal, parsed: ParsedPublish, expectedRevisionId?: string): Promise<{
|
|
155
|
+
record: ServerSkillRecord;
|
|
156
|
+
alreadyPublished: boolean;
|
|
157
|
+
}>;
|
|
147
158
|
/**
|
|
148
159
|
* Tombstone a published skill (todos d061fcda): the row survives with a tombstone
|
|
149
160
|
* marker for `tombstoneWindowMs`, so reads answer 410 and a pulling client can
|
package/dist/storage.js
CHANGED
|
@@ -65,7 +65,12 @@ import { join as join2, dirname } from "path";
|
|
|
65
65
|
// src/lib/retired-settings.ts
|
|
66
66
|
var RETIRED_ENV_SUFFIXES = ["_STORAGE_MODE", "_DEPLOYMENT_MODE", "_CLOUD_MODE"];
|
|
67
67
|
var RETIRED_CONFIG_KEYS = {
|
|
68
|
-
mode: "
|
|
68
|
+
mode: "a configured API origin",
|
|
69
|
+
apiUrl: "skills setup --api-url <origin>"
|
|
70
|
+
};
|
|
71
|
+
var RETIRED_CONFIG_KEY_REASONS = {
|
|
72
|
+
mode: "Deployment modes were removed: a Skills client either resolves a credential " + "or it does not, and that is the whole of it.",
|
|
73
|
+
apiUrl: "The service address is no longer kept in this app's config file: it is read from " + "HASNA_SKILLS_API_URL, then the macOS Keychain item hasna.credentials.skills.api-url, " + "then ~/.hasna/skills/config/credentials, then the fleet gateway."
|
|
69
74
|
};
|
|
70
75
|
|
|
71
76
|
class RetiredSettingError extends Error {
|
|
@@ -104,7 +109,7 @@ function assertNoRetiredConfigKeys(config, source) {
|
|
|
104
109
|
for (const [key, replacement] of Object.entries(RETIRED_CONFIG_KEYS)) {
|
|
105
110
|
if (!(key in config))
|
|
106
111
|
continue;
|
|
107
|
-
throw new RetiredSettingError(key, `${source}: "${key}" is no longer a configuration key. ` +
|
|
112
|
+
throw new RetiredSettingError(key, `${source}: "${key}" is no longer a configuration key. ` + `${RETIRED_CONFIG_KEY_REASONS[key] ?? ""} ` + `Use ${replacement} instead, and remove the old key with: skills config unset ${key}. ` + "Refused rather than ignored, because silently dropping it would leave an " + "operator believing they had pointed this install at a server.");
|
|
108
113
|
}
|
|
109
114
|
}
|
|
110
115
|
|
|
@@ -232,7 +237,7 @@ var ENUM_KEYS = {
|
|
|
232
237
|
defaultScope: ["global", "project"],
|
|
233
238
|
format: ["compact", "json", "csv"]
|
|
234
239
|
};
|
|
235
|
-
var STRING_KEYS = ["
|
|
240
|
+
var STRING_KEYS = ["extensionsDir"];
|
|
236
241
|
function validKeys() {
|
|
237
242
|
return [...Object.keys(ENUM_KEYS), ...STRING_KEYS];
|
|
238
243
|
}
|
|
@@ -263,16 +268,6 @@ function normalizeConfigValue(key, value) {
|
|
|
263
268
|
const allowed = allowedValues(key);
|
|
264
269
|
if (allowed)
|
|
265
270
|
return allowed.includes(value) ? value : undefined;
|
|
266
|
-
if (key === "apiUrl") {
|
|
267
|
-
try {
|
|
268
|
-
const url = new URL(value);
|
|
269
|
-
if (url.protocol !== "http:" && url.protocol !== "https:")
|
|
270
|
-
return;
|
|
271
|
-
return value.replace(/\/+$/, "");
|
|
272
|
-
} catch {
|
|
273
|
-
return;
|
|
274
|
-
}
|
|
275
|
-
}
|
|
276
271
|
if (key === "extensionsDir")
|
|
277
272
|
return value.trim() ? value : undefined;
|
|
278
273
|
return;
|
|
@@ -342,7 +337,7 @@ function saveConfig(key, value, scope = "project") {
|
|
|
342
337
|
const normalized = normalizeConfigValue(key, value);
|
|
343
338
|
if (normalized === undefined) {
|
|
344
339
|
const allowed = allowedValues(key);
|
|
345
|
-
throw new Error(allowed ? `Invalid value '${value}' for ${key}. Allowed: ${allowed.join(", ")}` : `Invalid value '${value}' for ${key}. Expected
|
|
340
|
+
throw new Error(allowed ? `Invalid value '${value}' for ${key}. Allowed: ${allowed.join(", ")}` : `Invalid value '${value}' for ${key}. Expected a non-empty path`);
|
|
346
341
|
}
|
|
347
342
|
const filePath = getConfigPath(scope);
|
|
348
343
|
let existing = {};
|
package/package.json
CHANGED
|
@@ -1,11 +1,12 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@hasna/skills",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.3.0",
|
|
4
4
|
"description": "Skills library for AI coding agents",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"bin": {
|
|
7
7
|
"skills": "bin/index.js",
|
|
8
8
|
"skills-mcp": "bin/mcp.js",
|
|
9
|
+
"skills-serve": "bin/server.js",
|
|
9
10
|
"skills-server": "bin/server.js",
|
|
10
11
|
"skills-worker": "bin/worker.js",
|
|
11
12
|
"skills-migrate": "bin/migrate.js"
|
|
@@ -91,6 +92,7 @@
|
|
|
91
92
|
"dependencies": {
|
|
92
93
|
"@aws-sdk/client-ecs": "^3.1079.0",
|
|
93
94
|
"@aws-sdk/client-s3": "^3.1079.0",
|
|
95
|
+
"@hasna/contracts": "1.0.1",
|
|
94
96
|
"@hasna/events": "0.1.16",
|
|
95
97
|
"@modelcontextprotocol/sdk": "^1.26.0",
|
|
96
98
|
"chalk": "^5.3.0",
|