@hasna/skills 0.8.11 → 0.9.1
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 +21 -4
- package/bin/index.js +853 -446
- package/bin/mcp.js +309 -155
- package/bin/migrate.js +100 -10
- package/bin/server.js +596 -51
- package/bin/worker.js +283 -20
- package/dist/cli/commands/registry.d.ts +1 -0
- package/dist/index.d.ts +1 -0
- package/dist/index.js +498 -184
- package/dist/lib/agent-discovery.d.ts +1 -1
- package/dist/lib/claude-settings-witness.d.ts +19 -0
- package/dist/lib/cloud-executions.d.ts +20 -0
- package/dist/lib/registry-types.d.ts +3 -0
- package/dist/lib/remote-client.d.ts +11 -0
- package/dist/lib/revision.d.ts +3 -0
- package/dist/sdk/execution/admission.d.ts +2 -1
- package/dist/sdk/execution/types.d.ts +9 -0
- package/dist/sdk/index.d.ts +1 -0
- package/dist/sdk/index.js +1755 -752
- package/dist/server/runtime-policy.d.ts +4 -2
- package/dist/server/runtime-pure-contract.d.ts +51 -0
- package/dist/server/runtime-store.d.ts +4 -0
- package/dist/server/runtime-worker.d.ts +2 -1
- package/dist/server/selection-store.d.ts +9 -1
- package/dist/server/skills-api.d.ts +2 -1
- package/dist/server/sqlite-store.d.ts +4 -1
- package/dist/server/store.d.ts +6 -1
- package/dist/server/types.d.ts +27 -0
- package/docs/plugin-admission.md +67 -0
- package/migrations/postgres/0010_skill_archive_lifecycle.sql +5 -0
- package/migrations/sqlite/0010_skill_archive_lifecycle.sql +5 -0
- package/package.json +1 -1
package/bin/mcp.js
CHANGED
|
@@ -20019,7 +20019,7 @@ var package_default;
|
|
|
20019
20019
|
var init_package = __esm(() => {
|
|
20020
20020
|
package_default = {
|
|
20021
20021
|
name: "@hasna/skills",
|
|
20022
|
-
version: "0.
|
|
20022
|
+
version: "0.9.1",
|
|
20023
20023
|
description: "Skills library for AI coding agents",
|
|
20024
20024
|
type: "module",
|
|
20025
20025
|
bin: {
|
|
@@ -37439,6 +37439,7 @@ function readManifest(skillPath, fallbackName, normalizeName) {
|
|
|
37439
37439
|
const version2 = readDeclaredSkillVersion(skillPath) ?? PORTABLE_SKILL_DEFAULT_VERSION;
|
|
37440
37440
|
const kind = parseSkillKind(stringField(jsonManifest, "kind") ?? frontmatter?.kind);
|
|
37441
37441
|
const commands = parseManifestCommands(jsonManifest) ?? (kind === "instruction" ? [] : inferPackageCommands(pkg, name)) ?? [];
|
|
37442
|
+
const runtime = parseManifestRuntime(jsonManifest) ?? inferRuntimeFromCommands(commands);
|
|
37442
37443
|
return {
|
|
37443
37444
|
$schema: stringField(jsonManifest, "$schema") ?? PORTABLE_SKILL_SCHEMA,
|
|
37444
37445
|
standard: stringField(jsonManifest, "standard") ?? PORTABLE_SKILL_STANDARD,
|
|
@@ -37451,7 +37452,7 @@ function readManifest(skillPath, fallbackName, normalizeName) {
|
|
|
37451
37452
|
...kind ? { kind } : {},
|
|
37452
37453
|
inputs: kind === "instruction" ? parseManifestInputs(jsonManifest) ?? [] : parseManifestInputs(jsonManifest) ?? DEFAULT_INPUTS,
|
|
37453
37454
|
commands,
|
|
37454
|
-
...
|
|
37455
|
+
...runtime ? { runtime } : {},
|
|
37455
37456
|
...parseManifestProvenance(jsonManifest) ? { provenance: parseManifestProvenance(jsonManifest) } : {}
|
|
37456
37457
|
};
|
|
37457
37458
|
}
|
|
@@ -37970,6 +37971,12 @@ function inferPackageCommands(pkg, fallbackName) {
|
|
|
37970
37971
|
}
|
|
37971
37972
|
return;
|
|
37972
37973
|
}
|
|
37974
|
+
function inferRuntimeFromCommands(commands) {
|
|
37975
|
+
const entrypoint = commands.find((command) => command.entry)?.entry;
|
|
37976
|
+
if (!entrypoint || !entrypoint.toLowerCase().endsWith(".py"))
|
|
37977
|
+
return;
|
|
37978
|
+
return { ...defaultRuntimeContract(entrypoint), runtime: "python3" };
|
|
37979
|
+
}
|
|
37973
37980
|
function readJsonObject(path) {
|
|
37974
37981
|
const parsed = JSON.parse(readFileSync8(path, "utf-8"));
|
|
37975
37982
|
if (!isRecord2(parsed))
|
|
@@ -38786,6 +38793,8 @@ function normalizeRemoteSkill(skill) {
|
|
|
38786
38793
|
tags: skill.tags || ["remote"],
|
|
38787
38794
|
dependencies: skill.dependencies,
|
|
38788
38795
|
...skill.version ? { version: skill.version } : {},
|
|
38796
|
+
...skill.revisionId !== undefined ? { revisionId: skill.revisionId } : {},
|
|
38797
|
+
...skill.lifecycle ? { lifecycle: skill.lifecycle } : {},
|
|
38789
38798
|
availability: normalizeRemoteAvailability(skill.availability),
|
|
38790
38799
|
source: "remote"
|
|
38791
38800
|
};
|
|
@@ -38889,6 +38898,8 @@ var init_remote_registry = __esm(() => {
|
|
|
38889
38898
|
tags: exports_external.array(exports_external.string()).optional(),
|
|
38890
38899
|
dependencies: exports_external.array(exports_external.string()).optional(),
|
|
38891
38900
|
version: exports_external.string().optional(),
|
|
38901
|
+
revisionId: exports_external.union([exports_external.string().regex(/^[\x21-\x7e]+$/), exports_external.null()]).optional(),
|
|
38902
|
+
lifecycle: exports_external.enum(["active", "archived"]).optional(),
|
|
38892
38903
|
availability: remoteAvailabilitySchema.optional()
|
|
38893
38904
|
}).passthrough().refine((skill) => skill.name || skill.slug, {
|
|
38894
38905
|
message: "Remote skill requires name or slug"
|
|
@@ -39944,7 +39955,9 @@ function assertAgentPolicyCollections(policy) {
|
|
|
39944
39955
|
text(source.path);
|
|
39945
39956
|
requireBound(source.sha256 === null || typeof source.sha256 === "string" && /^[a-f0-9]{64}$/.test(source.sha256));
|
|
39946
39957
|
if (source.hashMode !== undefined)
|
|
39947
|
-
requireBound(["bytes", "path-bytes", "claude-plugin-registry", "claude-marketplace-registry"].includes(source.hashMode) && source.format === undefined && source.fields === undefined && (source.hashMode === "bytes" || source.sha256 !== null));
|
|
39958
|
+
requireBound(["bytes", "path-bytes", "claude-plugin-registry", "claude-marketplace-registry", "claude-settings-v1"].includes(source.hashMode) && source.format === undefined && source.fields === undefined && (source.hashMode === "bytes" || source.sha256 !== null));
|
|
39959
|
+
if (source.hashMode === "claude-settings-v1")
|
|
39960
|
+
requireBound(agent === "claude" && value.agent === "claude" && value.method === "reviewed" && !/[\x00-\x1f\x7f]/.test(source.path) && isAbsolute4(source.path) && resolve4(source.path) === source.path && basename4(source.path) === "settings.json");
|
|
39948
39961
|
if (source.hashMode === "claude-marketplace-registry")
|
|
39949
39962
|
requireBound(agent === "claude" && value.agent === "claude" && value.method === "reviewed" && !/[\x00-\x1f\x7f]/.test(source.path) && isAbsolute4(source.path) && resolve4(source.path) === source.path && basename4(source.path) === "known_marketplaces.json");
|
|
39950
39963
|
if (source.hashMode === "claude-plugin-registry") {
|
|
@@ -47592,6 +47605,63 @@ var init_claude_marketplace_registry = __esm(() => {
|
|
|
47592
47605
|
CLAUDE_MARKETPLACE_REGISTRY_LIMITS = Object.freeze({ bytes: 1024 * 1024, rows: 1024, depth: 32, nodes: 65536, stringCharacters: 16384, pathCharacters: 4096 });
|
|
47593
47606
|
});
|
|
47594
47607
|
|
|
47608
|
+
// src/lib/claude-settings-witness.ts
|
|
47609
|
+
var CLAUDE_SETTINGS_WITNESS_LIMITS, BOOLEAN_PREFERENCES, ENUM_PREFERENCES, BUILTIN_MODEL_SELECTIONS;
|
|
47610
|
+
var init_claude_settings_witness = __esm(() => {
|
|
47611
|
+
CLAUDE_SETTINGS_WITNESS_LIMITS = Object.freeze({ bytes: 1024 * 1024, rows: 1024, depth: 32, nodes: 65536, stringCharacters: 16384, pathCharacters: 4096 });
|
|
47612
|
+
BOOLEAN_PREFERENCES = new Set([
|
|
47613
|
+
"autoScrollEnabled",
|
|
47614
|
+
"axScreenReader",
|
|
47615
|
+
"emojiCompletionEnabled",
|
|
47616
|
+
"prefersReducedMotion",
|
|
47617
|
+
"showTurnDuration",
|
|
47618
|
+
"spinnerTipsEnabled",
|
|
47619
|
+
"syntaxHighlightingDisabled",
|
|
47620
|
+
"terminalProgressBarEnabled",
|
|
47621
|
+
"terminalTitleFromRename",
|
|
47622
|
+
"verbose",
|
|
47623
|
+
"wheelScrollAccelerationEnabled"
|
|
47624
|
+
]);
|
|
47625
|
+
ENUM_PREFERENCES = Object.freeze({
|
|
47626
|
+
editorMode: ["normal", "vim"],
|
|
47627
|
+
tui: ["default", "fullscreen"],
|
|
47628
|
+
viewMode: ["default", "verbose", "focus"]
|
|
47629
|
+
});
|
|
47630
|
+
BUILTIN_MODEL_SELECTIONS = new Set([
|
|
47631
|
+
"default",
|
|
47632
|
+
"best",
|
|
47633
|
+
"fable",
|
|
47634
|
+
"fable[1m]",
|
|
47635
|
+
"sonnet",
|
|
47636
|
+
"sonnet[1m]",
|
|
47637
|
+
"opus",
|
|
47638
|
+
"opus[1m]",
|
|
47639
|
+
"haiku",
|
|
47640
|
+
"opusplan",
|
|
47641
|
+
"claude-fable-5-1",
|
|
47642
|
+
"claude-fable-5",
|
|
47643
|
+
"claude-fable-5[1m]",
|
|
47644
|
+
"claude-opus-5",
|
|
47645
|
+
"claude-sonnet-5",
|
|
47646
|
+
"claude-haiku-4-5-20251001",
|
|
47647
|
+
"claude-opus-4-6",
|
|
47648
|
+
"claude-sonnet-4-5",
|
|
47649
|
+
"claude-sonnet-4-5-20250929",
|
|
47650
|
+
"claude-opus-4-8",
|
|
47651
|
+
"claude-opus-4-8[1m]",
|
|
47652
|
+
"claude-opus-4-7",
|
|
47653
|
+
"claude-sonnet-4-6",
|
|
47654
|
+
"claude-opus-4-5-20251101",
|
|
47655
|
+
"claude-opus-4-5",
|
|
47656
|
+
"claude-haiku-4-5",
|
|
47657
|
+
"claude-fable-5-1[1m]",
|
|
47658
|
+
"claude-opus-5[1m]",
|
|
47659
|
+
"claude-opus-4-7[1m]",
|
|
47660
|
+
"claude-opus-4-6[1m]",
|
|
47661
|
+
"claude-sonnet-4-6[1m]"
|
|
47662
|
+
]);
|
|
47663
|
+
});
|
|
47664
|
+
|
|
47595
47665
|
// src/lib/agent-discovery.ts
|
|
47596
47666
|
var init_agent_discovery = __esm(() => {
|
|
47597
47667
|
init_agent_policy_limits();
|
|
@@ -47602,6 +47672,7 @@ var init_agent_discovery = __esm(() => {
|
|
|
47602
47672
|
init_plugin_discovery();
|
|
47603
47673
|
init_plugin_admission();
|
|
47604
47674
|
init_claude_marketplace_registry();
|
|
47675
|
+
init_claude_settings_witness();
|
|
47605
47676
|
init_agent_discovery_directories();
|
|
47606
47677
|
});
|
|
47607
47678
|
|
|
@@ -49467,6 +49538,7 @@ __export(exports_remote_client, {
|
|
|
49467
49538
|
RemoteWorkspaceSelectionError: () => RemoteWorkspaceSelectionError,
|
|
49468
49539
|
RemoteWorkspaceMemberError: () => RemoteWorkspaceMemberError,
|
|
49469
49540
|
RemoteSkillsClient: () => RemoteSkillsClient,
|
|
49541
|
+
RemoteSkillLifecycleError: () => RemoteSkillLifecycleError,
|
|
49470
49542
|
RemoteRouteUnsupportedError: () => RemoteRouteUnsupportedError,
|
|
49471
49543
|
RemoteRequestError: () => RemoteRequestError,
|
|
49472
49544
|
RemoteQuoteUnavailableError: () => RemoteQuoteUnavailableError,
|
|
@@ -49598,6 +49670,39 @@ class RemoteSkillsClient {
|
|
|
49598
49670
|
})();
|
|
49599
49671
|
return this.capabilities;
|
|
49600
49672
|
}
|
|
49673
|
+
async addSkillPublishScope(keyId, expectedScopes, expectedOrgId) {
|
|
49674
|
+
if (!/^[A-Za-z0-9_-]+$/.test(keyId))
|
|
49675
|
+
throw new Error("Invalid API key id");
|
|
49676
|
+
if (!/^[A-Za-z0-9_-]{1,256}$/.test(expectedOrgId))
|
|
49677
|
+
throw new Error("Invalid expected organization id");
|
|
49678
|
+
if (!Array.isArray(expectedScopes) || expectedScopes.length > 32 || expectedScopes.some((scope) => typeof scope !== "string" || scope.length > 128 || !/^(?:\*|[a-z][a-z0-9_-]*:(?:\*|[a-z][a-z0-9_-]*))$/.test(scope)) || new Set(expectedScopes).size !== expectedScopes.length)
|
|
49679
|
+
throw new Error("Invalid expected API key scopes");
|
|
49680
|
+
const path = `/api/v1/admin/keys/${encodeURIComponent(keyId)}/scopes`;
|
|
49681
|
+
const response = await this.request(path, { method: "PATCH", body: JSON.stringify({ expected_scopes: expectedScopes, add_scopes: ["skills:publish"] }) });
|
|
49682
|
+
if (!response.ok)
|
|
49683
|
+
throw new RemoteRequestError(path, response.status);
|
|
49684
|
+
let value;
|
|
49685
|
+
try {
|
|
49686
|
+
value = JSON.parse(new TextDecoder("utf-8", { fatal: true }).decode(await readBoundedResponse(response, 64 * 1024)));
|
|
49687
|
+
} catch {
|
|
49688
|
+
throw new Error("Invalid API key scope update response");
|
|
49689
|
+
}
|
|
49690
|
+
if (!value || typeof value !== "object" || Array.isArray(value))
|
|
49691
|
+
throw new Error("Invalid API key scope update response");
|
|
49692
|
+
const body = value;
|
|
49693
|
+
const returnedId = body.keyId;
|
|
49694
|
+
const orgId = body.orgId;
|
|
49695
|
+
const scopes = body.scopes;
|
|
49696
|
+
if (returnedId !== keyId || typeof orgId !== "string" || !orgId || expectedOrgId !== undefined && orgId !== expectedOrgId || !Array.isArray(scopes) || scopes.some((scope) => typeof scope !== "string") || body.updated !== true) {
|
|
49697
|
+
throw new Error("Invalid API key scope update response");
|
|
49698
|
+
}
|
|
49699
|
+
const returnedScopes = scopes;
|
|
49700
|
+
const expectedResult = expectedScopes.includes("skills:publish") ? expectedScopes : [...expectedScopes, "skills:publish"];
|
|
49701
|
+
if (returnedScopes.length !== expectedResult.length || expectedResult.some((scope, index) => returnedScopes[index] !== scope)) {
|
|
49702
|
+
throw new Error("API key scope update response did not preserve the expected scopes");
|
|
49703
|
+
}
|
|
49704
|
+
return { keyId, orgId, scopes: [...returnedScopes], updated: true };
|
|
49705
|
+
}
|
|
49601
49706
|
async submitQuotedRun(slug2, input2 = {}, args = [], approval = {}) {
|
|
49602
49707
|
runQuoteReceipt(approval.quoteReceipt);
|
|
49603
49708
|
({ input: input2, args, approval } = JSON.parse(JSON.stringify({ input: input2, args, approval })));
|
|
@@ -50115,6 +50220,19 @@ class RemoteSkillsClient {
|
|
|
50115
50220
|
async deleteSkill(slug2) {
|
|
50116
50221
|
return this.request(`/api/v1/skills/${encodeURIComponent(slug2)}`, { method: "DELETE" });
|
|
50117
50222
|
}
|
|
50223
|
+
async setSkillLifecycle(slug2, lifecycle, options) {
|
|
50224
|
+
const path = `/api/v1/skills/${encodeURIComponent(slug2)}/lifecycle`;
|
|
50225
|
+
const response = await this.request(path, {
|
|
50226
|
+
method: "PATCH",
|
|
50227
|
+
headers: { "If-Match": options.expectedRevisionId },
|
|
50228
|
+
body: JSON.stringify({ lifecycle, ...options.reason ? { reason: options.reason } : {}, ...options.replacementSlug ? { replacementSlug: options.replacementSlug } : {} })
|
|
50229
|
+
});
|
|
50230
|
+
if (!response.ok) {
|
|
50231
|
+
const code = await readResponseCode(response);
|
|
50232
|
+
throw new RemoteSkillLifecycleError(path, response.status, code);
|
|
50233
|
+
}
|
|
50234
|
+
return response.json();
|
|
50235
|
+
}
|
|
50118
50236
|
async downloadSkillBundle(slug2) {
|
|
50119
50237
|
return this.request(`/api/v1/skills/${encodeURIComponent(slug2)}/bundle`, { method: "GET" });
|
|
50120
50238
|
}
|
|
@@ -50266,9 +50384,13 @@ function normalizeSkillSummaryList(payload) {
|
|
|
50266
50384
|
return payload.map(normalizeSkillSummary);
|
|
50267
50385
|
}
|
|
50268
50386
|
async function responseBodyCarriesCode(response, codes) {
|
|
50387
|
+
const code = await readResponseCode(response);
|
|
50388
|
+
return code !== undefined && codes.includes(code);
|
|
50389
|
+
}
|
|
50390
|
+
async function readResponseCode(response) {
|
|
50269
50391
|
const reader = response.body?.getReader();
|
|
50270
50392
|
if (!reader)
|
|
50271
|
-
return
|
|
50393
|
+
return;
|
|
50272
50394
|
const maximum = 8 * 1024;
|
|
50273
50395
|
let deadline;
|
|
50274
50396
|
const expired = new Promise((_, reject) => {
|
|
@@ -50283,7 +50405,7 @@ async function responseBodyCarriesCode(response, codes) {
|
|
|
50283
50405
|
break;
|
|
50284
50406
|
size += next.value.byteLength;
|
|
50285
50407
|
if (size > maximum)
|
|
50286
|
-
return
|
|
50408
|
+
return;
|
|
50287
50409
|
chunks.push(next.value);
|
|
50288
50410
|
}
|
|
50289
50411
|
const bytes = new Uint8Array(size);
|
|
@@ -50294,11 +50416,11 @@ async function responseBodyCarriesCode(response, codes) {
|
|
|
50294
50416
|
}
|
|
50295
50417
|
const payload = JSON.parse(new TextDecoder("utf-8", { fatal: true }).decode(bytes));
|
|
50296
50418
|
if (!payload || typeof payload !== "object" || Array.isArray(payload) || !Object.hasOwn(payload, "code"))
|
|
50297
|
-
return
|
|
50419
|
+
return;
|
|
50298
50420
|
const code = payload.code;
|
|
50299
|
-
return typeof code === "string" &&
|
|
50421
|
+
return typeof code === "string" && /^[A-Z][A-Z0-9_:-]{0,127}$/.test(code) ? code : undefined;
|
|
50300
50422
|
} catch {
|
|
50301
|
-
return
|
|
50423
|
+
return;
|
|
50302
50424
|
} finally {
|
|
50303
50425
|
clearTimeout(deadline);
|
|
50304
50426
|
reader.cancel().catch(() => {});
|
|
@@ -50327,7 +50449,7 @@ async function createRemoteSkillsClient(env = process.env) {
|
|
|
50327
50449
|
function createRemoteSkillsClientReadOnly(env = process.env) {
|
|
50328
50450
|
return createRemoteSkillsClient(env);
|
|
50329
50451
|
}
|
|
50330
|
-
var RemoteRouteUnsupportedError, RemoteRequestError, RemoteCreditCheckoutError, RemoteQuoteUnavailableError, RemoteWorkspaceMemberError, RemoteWorkspaceSelectionError, RemoteCapabilityUnavailableError, INVALID_SKILL_VERSION_RESPONSE = "Remote skill version payload did not match the expected contract.";
|
|
50452
|
+
var RemoteRouteUnsupportedError, RemoteRequestError, KNOWN_SKILL_LIFECYCLE_CODES, RemoteSkillLifecycleError, RemoteCreditCheckoutError, RemoteQuoteUnavailableError, RemoteWorkspaceMemberError, RemoteWorkspaceSelectionError, RemoteCapabilityUnavailableError, INVALID_SKILL_VERSION_RESPONSE = "Remote skill version payload did not match the expected contract.";
|
|
50331
50453
|
var init_remote_client = __esm(() => {
|
|
50332
50454
|
init_remote_recurring();
|
|
50333
50455
|
init_remote_invitations();
|
|
@@ -50364,6 +50486,17 @@ var init_remote_client = __esm(() => {
|
|
|
50364
50486
|
this.name = "RemoteRequestError";
|
|
50365
50487
|
}
|
|
50366
50488
|
};
|
|
50489
|
+
KNOWN_SKILL_LIFECYCLE_CODES = new Set(["SKILL_ARCHIVE_PROFILE_CONFLICT", "LIFECYCLE_ROLE_REQUIRED"]);
|
|
50490
|
+
RemoteSkillLifecycleError = class RemoteSkillLifecycleError extends RemoteRequestError {
|
|
50491
|
+
code;
|
|
50492
|
+
constructor(path, status, code) {
|
|
50493
|
+
super(path, status);
|
|
50494
|
+
this.name = "RemoteSkillLifecycleError";
|
|
50495
|
+
const safeCode = code !== undefined && KNOWN_SKILL_LIFECYCLE_CODES.has(code) ? code : undefined;
|
|
50496
|
+
this.code = safeCode;
|
|
50497
|
+
this.message = `Skill lifecycle update was refused (HTTP ${status}${safeCode ? `, code ${safeCode}` : ""})`;
|
|
50498
|
+
}
|
|
50499
|
+
};
|
|
50367
50500
|
RemoteCreditCheckoutError = class RemoteCreditCheckoutError extends RemoteRequestError {
|
|
50368
50501
|
code;
|
|
50369
50502
|
requestIdempotencyKey;
|
|
@@ -53758,6 +53891,161 @@ var init_run_routing = __esm(() => {
|
|
|
53758
53891
|
init_fleet_credentials();
|
|
53759
53892
|
});
|
|
53760
53893
|
|
|
53894
|
+
// src/lib/cloud-executions.ts
|
|
53895
|
+
var exports_cloud_executions = {};
|
|
53896
|
+
__export(exports_cloud_executions, {
|
|
53897
|
+
CloudExecutionClient: () => CloudExecutionClient
|
|
53898
|
+
});
|
|
53899
|
+
|
|
53900
|
+
class CloudExecutionClient {
|
|
53901
|
+
apiOrigin;
|
|
53902
|
+
apiKey;
|
|
53903
|
+
constructor(apiOrigin, apiKey) {
|
|
53904
|
+
this.apiOrigin = apiOrigin;
|
|
53905
|
+
this.apiKey = apiKey;
|
|
53906
|
+
}
|
|
53907
|
+
static async configured() {
|
|
53908
|
+
const c = await resolveSkillsConnection();
|
|
53909
|
+
if (!c)
|
|
53910
|
+
throw Error("Cloud execution requires a configured Skills API credential");
|
|
53911
|
+
return new CloudExecutionClient(c.apiOrigin, c.apiKey);
|
|
53912
|
+
}
|
|
53913
|
+
async request(route, method = "GET", value) {
|
|
53914
|
+
const response = await fetch(skillsApiRequestUrl(this.apiOrigin, "/api/v1/executions/" + route), {
|
|
53915
|
+
method,
|
|
53916
|
+
headers: {
|
|
53917
|
+
authorization: `Bearer ${this.apiKey}`,
|
|
53918
|
+
"User-Agent": `hasna-skills/${package_default.version}`,
|
|
53919
|
+
...value === undefined ? {} : { "content-type": "application/json" }
|
|
53920
|
+
},
|
|
53921
|
+
...value === undefined ? {} : { body: JSON.stringify(value) },
|
|
53922
|
+
redirect: "error",
|
|
53923
|
+
signal: AbortSignal.timeout(15000)
|
|
53924
|
+
});
|
|
53925
|
+
if (!response.ok) {
|
|
53926
|
+
response.body?.cancel();
|
|
53927
|
+
throw Error(`Skills cloud execution refused: HTTP ${response.status}`);
|
|
53928
|
+
}
|
|
53929
|
+
return response;
|
|
53930
|
+
}
|
|
53931
|
+
async eligibility(slug2, version2, bundleDigest) {
|
|
53932
|
+
if (!/^[a-z0-9-]+$/.test(slug2) || !/^\d+\.\d+\.\d+$/.test(version2) || bundleDigest !== undefined && !/^(sha256:)?[a-f0-9]{64}$/.test(bundleDigest))
|
|
53933
|
+
throw Error("Cloud eligibility requires an exact skill version and valid digest");
|
|
53934
|
+
const query = new URLSearchParams({ version: version2, ...bundleDigest ? { bundleDigest } : {} });
|
|
53935
|
+
const v = await jsonBody(await this.request(`${slug2}/eligibility?${query}`));
|
|
53936
|
+
if (!v || v.contractVersion !== 1 || typeof v.eligible !== "boolean" || v.skill !== slug2 || v.version !== version2 || !/^[a-f0-9]{64}$/.test(v.bundleDigest) || bundleDigest && bundleDigest.replace(/^sha256:/, "") !== v.bundleDigest || v.eligible && (!v.executionContract || !["pdf.v1", "regex-test.v1"].includes(v.executionContract.id)))
|
|
53937
|
+
throw Error("Invalid cloud eligibility receipt");
|
|
53938
|
+
return v;
|
|
53939
|
+
}
|
|
53940
|
+
async submit(slug2, version2, input2, key, selection) {
|
|
53941
|
+
if (!/^[a-z0-9-]+$/.test(slug2) || !/^\d+\.\d+\.\d+$/.test(version2))
|
|
53942
|
+
throw Error("Cloud execution requires skill@exact-version");
|
|
53943
|
+
if (!/^[A-Za-z0-9._:-]{1,128}$/.test(key))
|
|
53944
|
+
throw Error("Invalid idempotency key");
|
|
53945
|
+
if (selection?.authority && selection.authority !== skillsApiRequestUrl(this.apiOrigin, "/api/v1/").replace(/\/$/, ""))
|
|
53946
|
+
throw Error("Selected skill belongs to another Skills authority");
|
|
53947
|
+
const execution = parseExecution(await jsonBody(await this.request(slug2, "POST", {
|
|
53948
|
+
version: version2,
|
|
53949
|
+
input: input2,
|
|
53950
|
+
idempotencyKey: key,
|
|
53951
|
+
...selection ? {
|
|
53952
|
+
bundleDigest: selection.bundleDigest,
|
|
53953
|
+
workspaceId: selection.workspaceId
|
|
53954
|
+
} : {}
|
|
53955
|
+
})));
|
|
53956
|
+
if (execution.skill !== slug2 || execution.version !== version2 || selection && execution.bundleDigest.replace(/^sha256:/, "") !== selection.bundleDigest.replace(/^sha256:/, ""))
|
|
53957
|
+
throw Error("Cloud execution receipt does not match the selected exact skill");
|
|
53958
|
+
return execution;
|
|
53959
|
+
}
|
|
53960
|
+
async get(id) {
|
|
53961
|
+
return parseExecution(await jsonBody(await this.request(runId(id))));
|
|
53962
|
+
}
|
|
53963
|
+
async logs(id) {
|
|
53964
|
+
return jsonBody(await this.request(runId(id) + "/logs"));
|
|
53965
|
+
}
|
|
53966
|
+
async artifacts(id) {
|
|
53967
|
+
return (await this.get(id)).artifacts;
|
|
53968
|
+
}
|
|
53969
|
+
async download(id, name) {
|
|
53970
|
+
if (!["document.pdf", "document.html"].includes(name))
|
|
53971
|
+
throw Error("Invalid cloud artifact name");
|
|
53972
|
+
const response = await this.request(runId(id) + "/artifacts/" + encodeURIComponent(name));
|
|
53973
|
+
if (!response.body)
|
|
53974
|
+
throw Error("Cloud artifact body is missing");
|
|
53975
|
+
const reader = response.body.getReader();
|
|
53976
|
+
const chunks = [];
|
|
53977
|
+
let size = 0;
|
|
53978
|
+
for (;; ) {
|
|
53979
|
+
const r2 = await reader.read();
|
|
53980
|
+
if (r2.done)
|
|
53981
|
+
break;
|
|
53982
|
+
size += r2.value.length;
|
|
53983
|
+
if (size > 2000000) {
|
|
53984
|
+
await reader.cancel();
|
|
53985
|
+
throw Error("Cloud artifact exceeds limit");
|
|
53986
|
+
}
|
|
53987
|
+
chunks.push(r2.value);
|
|
53988
|
+
}
|
|
53989
|
+
return Buffer.concat(chunks);
|
|
53990
|
+
}
|
|
53991
|
+
async cancel(id) {
|
|
53992
|
+
return parseExecution(await jsonBody(await this.request(runId(id) + "/cancel", "POST", {})));
|
|
53993
|
+
}
|
|
53994
|
+
async wait(id, options = {}) {
|
|
53995
|
+
const timeout = options.timeoutMs ?? 300000, interval = options.intervalMs ?? 1000;
|
|
53996
|
+
if (!Number.isFinite(timeout) || timeout < 1 || timeout > 1200000 || !Number.isFinite(interval) || interval < 100 || interval > 60000)
|
|
53997
|
+
throw Error("Invalid cloud polling bounds");
|
|
53998
|
+
const until = Date.now() + timeout;
|
|
53999
|
+
for (;; ) {
|
|
54000
|
+
const run = await this.get(id);
|
|
54001
|
+
if (["succeeded", "failed", "cancelled"].includes(run.status))
|
|
54002
|
+
return run;
|
|
54003
|
+
if (Date.now() >= until)
|
|
54004
|
+
throw Error("Cloud execution is still running; poll the returned execution id");
|
|
54005
|
+
await Bun.sleep(interval);
|
|
54006
|
+
}
|
|
54007
|
+
}
|
|
54008
|
+
}
|
|
54009
|
+
function runId(id) {
|
|
54010
|
+
if (!/^run_[a-z0-9_]+$/.test(id))
|
|
54011
|
+
throw Error("Invalid cloud execution id");
|
|
54012
|
+
return id;
|
|
54013
|
+
}
|
|
54014
|
+
function parseExecution(value) {
|
|
54015
|
+
const v = value;
|
|
54016
|
+
if (!v || v.contractVersion !== 1 || v.target !== "cloud" || ![
|
|
54017
|
+
"admitted",
|
|
54018
|
+
"leased",
|
|
54019
|
+
"running",
|
|
54020
|
+
"succeeded",
|
|
54021
|
+
"failed",
|
|
54022
|
+
"cancelled"
|
|
54023
|
+
].includes(v.status) || typeof v.skill !== "string" || typeof v.version !== "string" || !Array.isArray(v.artifacts))
|
|
54024
|
+
throw Error("Unsupported cloud execution response");
|
|
54025
|
+
runId(v.id);
|
|
54026
|
+
for (const field of [
|
|
54027
|
+
"bundleDigest",
|
|
54028
|
+
"inputDigest",
|
|
54029
|
+
"runtimeImageDigest"
|
|
54030
|
+
])
|
|
54031
|
+
if (typeof v[field] !== "string" || !/^(sha256:)?[a-f0-9]{64}$/.test(v[field]))
|
|
54032
|
+
throw Error("Invalid cloud execution integrity receipt");
|
|
54033
|
+
return v;
|
|
54034
|
+
}
|
|
54035
|
+
async function jsonBody(response) {
|
|
54036
|
+
const bytes = await readBoundedResponse(response, 1e5);
|
|
54037
|
+
try {
|
|
54038
|
+
return JSON.parse(new TextDecoder().decode(bytes));
|
|
54039
|
+
} catch {
|
|
54040
|
+
throw Error("Invalid cloud execution JSON response");
|
|
54041
|
+
}
|
|
54042
|
+
}
|
|
54043
|
+
var init_cloud_executions = __esm(() => {
|
|
54044
|
+
init_package();
|
|
54045
|
+
init_remote_files();
|
|
54046
|
+
init_fleet_credentials();
|
|
54047
|
+
});
|
|
54048
|
+
|
|
53761
54049
|
// node_modules/@hasna/secrets/dist/sdk.js
|
|
53762
54050
|
import { isIP as isIP2 } from "net";
|
|
53763
54051
|
import { spawnSync as spawnSync2 } from "child_process";
|
|
@@ -55493,152 +55781,6 @@ var init_selected_run = __esm(() => {
|
|
|
55493
55781
|
init_execution_secrets();
|
|
55494
55782
|
});
|
|
55495
55783
|
|
|
55496
|
-
// src/lib/cloud-executions.ts
|
|
55497
|
-
var exports_cloud_executions = {};
|
|
55498
|
-
__export(exports_cloud_executions, {
|
|
55499
|
-
CloudExecutionClient: () => CloudExecutionClient
|
|
55500
|
-
});
|
|
55501
|
-
|
|
55502
|
-
class CloudExecutionClient {
|
|
55503
|
-
apiOrigin;
|
|
55504
|
-
apiKey;
|
|
55505
|
-
constructor(apiOrigin, apiKey) {
|
|
55506
|
-
this.apiOrigin = apiOrigin;
|
|
55507
|
-
this.apiKey = apiKey;
|
|
55508
|
-
}
|
|
55509
|
-
static async configured() {
|
|
55510
|
-
const c = await resolveSkillsConnection();
|
|
55511
|
-
if (!c)
|
|
55512
|
-
throw Error("Cloud execution requires a configured Skills API credential");
|
|
55513
|
-
return new CloudExecutionClient(c.apiOrigin, c.apiKey);
|
|
55514
|
-
}
|
|
55515
|
-
async request(route, method = "GET", value) {
|
|
55516
|
-
const response = await fetch(skillsApiRequestUrl(this.apiOrigin, "/api/v1/executions/" + route), {
|
|
55517
|
-
method,
|
|
55518
|
-
headers: {
|
|
55519
|
-
authorization: `Bearer ${this.apiKey}`,
|
|
55520
|
-
"User-Agent": `hasna-skills/${package_default.version}`,
|
|
55521
|
-
...value === undefined ? {} : { "content-type": "application/json" }
|
|
55522
|
-
},
|
|
55523
|
-
...value === undefined ? {} : { body: JSON.stringify(value) },
|
|
55524
|
-
redirect: "error",
|
|
55525
|
-
signal: AbortSignal.timeout(15000)
|
|
55526
|
-
});
|
|
55527
|
-
if (!response.ok) {
|
|
55528
|
-
response.body?.cancel();
|
|
55529
|
-
throw Error(`Skills cloud execution refused: HTTP ${response.status}`);
|
|
55530
|
-
}
|
|
55531
|
-
return response;
|
|
55532
|
-
}
|
|
55533
|
-
async submit(slug2, version2, input2, key, selection2) {
|
|
55534
|
-
if (!/^[a-z0-9-]+$/.test(slug2) || !/^\d+\.\d+\.\d+$/.test(version2))
|
|
55535
|
-
throw Error("Cloud execution requires skill@exact-version");
|
|
55536
|
-
if (!/^[A-Za-z0-9._:-]{1,128}$/.test(key))
|
|
55537
|
-
throw Error("Invalid idempotency key");
|
|
55538
|
-
if (selection2?.authority && selection2.authority !== skillsApiRequestUrl(this.apiOrigin, "/api/v1/").replace(/\/$/, ""))
|
|
55539
|
-
throw Error("Selected skill belongs to another Skills authority");
|
|
55540
|
-
const execution = parseExecution(await jsonBody(await this.request(slug2, "POST", {
|
|
55541
|
-
version: version2,
|
|
55542
|
-
input: input2,
|
|
55543
|
-
idempotencyKey: key,
|
|
55544
|
-
...selection2 ? {
|
|
55545
|
-
bundleDigest: selection2.bundleDigest,
|
|
55546
|
-
workspaceId: selection2.workspaceId
|
|
55547
|
-
} : {}
|
|
55548
|
-
})));
|
|
55549
|
-
if (execution.skill !== slug2 || execution.version !== version2 || selection2 && execution.bundleDigest.replace(/^sha256:/, "") !== selection2.bundleDigest.replace(/^sha256:/, ""))
|
|
55550
|
-
throw Error("Cloud execution receipt does not match the selected exact skill");
|
|
55551
|
-
return execution;
|
|
55552
|
-
}
|
|
55553
|
-
async get(id2) {
|
|
55554
|
-
return parseExecution(await jsonBody(await this.request(runId(id2))));
|
|
55555
|
-
}
|
|
55556
|
-
async logs(id2) {
|
|
55557
|
-
return jsonBody(await this.request(runId(id2) + "/logs"));
|
|
55558
|
-
}
|
|
55559
|
-
async artifacts(id2) {
|
|
55560
|
-
return (await this.get(id2)).artifacts;
|
|
55561
|
-
}
|
|
55562
|
-
async download(id2, name) {
|
|
55563
|
-
if (!["document.pdf", "document.html"].includes(name))
|
|
55564
|
-
throw Error("Invalid cloud artifact name");
|
|
55565
|
-
const response = await this.request(runId(id2) + "/artifacts/" + encodeURIComponent(name));
|
|
55566
|
-
if (!response.body)
|
|
55567
|
-
throw Error("Cloud artifact body is missing");
|
|
55568
|
-
const reader = response.body.getReader();
|
|
55569
|
-
const chunks = [];
|
|
55570
|
-
let size = 0;
|
|
55571
|
-
for (;; ) {
|
|
55572
|
-
const r2 = await reader.read();
|
|
55573
|
-
if (r2.done)
|
|
55574
|
-
break;
|
|
55575
|
-
size += r2.value.length;
|
|
55576
|
-
if (size > 2000000) {
|
|
55577
|
-
await reader.cancel();
|
|
55578
|
-
throw Error("Cloud artifact exceeds limit");
|
|
55579
|
-
}
|
|
55580
|
-
chunks.push(r2.value);
|
|
55581
|
-
}
|
|
55582
|
-
return Buffer.concat(chunks);
|
|
55583
|
-
}
|
|
55584
|
-
async cancel(id2) {
|
|
55585
|
-
return parseExecution(await jsonBody(await this.request(runId(id2) + "/cancel", "POST", {})));
|
|
55586
|
-
}
|
|
55587
|
-
async wait(id2, options = {}) {
|
|
55588
|
-
const timeout = options.timeoutMs ?? 300000, interval = options.intervalMs ?? 1000;
|
|
55589
|
-
if (!Number.isFinite(timeout) || timeout < 1 || timeout > 1200000 || !Number.isFinite(interval) || interval < 100 || interval > 60000)
|
|
55590
|
-
throw Error("Invalid cloud polling bounds");
|
|
55591
|
-
const until = Date.now() + timeout;
|
|
55592
|
-
for (;; ) {
|
|
55593
|
-
const run = await this.get(id2);
|
|
55594
|
-
if (["succeeded", "failed", "cancelled"].includes(run.status))
|
|
55595
|
-
return run;
|
|
55596
|
-
if (Date.now() >= until)
|
|
55597
|
-
throw Error("Cloud execution is still running; poll the returned execution id");
|
|
55598
|
-
await Bun.sleep(interval);
|
|
55599
|
-
}
|
|
55600
|
-
}
|
|
55601
|
-
}
|
|
55602
|
-
function runId(id2) {
|
|
55603
|
-
if (!/^run_[a-z0-9_]+$/.test(id2))
|
|
55604
|
-
throw Error("Invalid cloud execution id");
|
|
55605
|
-
return id2;
|
|
55606
|
-
}
|
|
55607
|
-
function parseExecution(value) {
|
|
55608
|
-
const v = value;
|
|
55609
|
-
if (!v || v.contractVersion !== 1 || v.target !== "cloud" || ![
|
|
55610
|
-
"admitted",
|
|
55611
|
-
"leased",
|
|
55612
|
-
"running",
|
|
55613
|
-
"succeeded",
|
|
55614
|
-
"failed",
|
|
55615
|
-
"cancelled"
|
|
55616
|
-
].includes(v.status) || typeof v.skill !== "string" || typeof v.version !== "string" || !Array.isArray(v.artifacts))
|
|
55617
|
-
throw Error("Unsupported cloud execution response");
|
|
55618
|
-
runId(v.id);
|
|
55619
|
-
for (const field of [
|
|
55620
|
-
"bundleDigest",
|
|
55621
|
-
"inputDigest",
|
|
55622
|
-
"runtimeImageDigest"
|
|
55623
|
-
])
|
|
55624
|
-
if (typeof v[field] !== "string" || !/^(sha256:)?[a-f0-9]{64}$/.test(v[field]))
|
|
55625
|
-
throw Error("Invalid cloud execution integrity receipt");
|
|
55626
|
-
return v;
|
|
55627
|
-
}
|
|
55628
|
-
async function jsonBody(response) {
|
|
55629
|
-
const bytes = await readBoundedResponse(response, 1e5);
|
|
55630
|
-
try {
|
|
55631
|
-
return JSON.parse(new TextDecoder().decode(bytes));
|
|
55632
|
-
} catch {
|
|
55633
|
-
throw Error("Invalid cloud execution JSON response");
|
|
55634
|
-
}
|
|
55635
|
-
}
|
|
55636
|
-
var init_cloud_executions = __esm(() => {
|
|
55637
|
-
init_package();
|
|
55638
|
-
init_remote_files();
|
|
55639
|
-
init_fleet_credentials();
|
|
55640
|
-
});
|
|
55641
|
-
|
|
55642
55784
|
// src/lib/blog-article.ts
|
|
55643
55785
|
var exports_blog_article = {};
|
|
55644
55786
|
__export(exports_blog_article, {
|
|
@@ -55773,6 +55915,18 @@ var init_blog_article = __esm(() => {
|
|
|
55773
55915
|
import { existsSync as existsSync15, readdirSync as readdirSync9, statSync as statSync10 } from "fs";
|
|
55774
55916
|
import { join as join24 } from "path";
|
|
55775
55917
|
function registerOperationTools(server) {
|
|
55918
|
+
server.registerTool("cloud_skill_eligibility", {
|
|
55919
|
+
title: "Cloud Skill Eligibility",
|
|
55920
|
+
description: "Inspect the authenticated workspace's exact reviewed cloud contract. Does not execute or admit a skill.",
|
|
55921
|
+
inputSchema: { name: exports_external.string(), version: exports_external.string(), bundleDigest: exports_external.string().optional() }
|
|
55922
|
+
}, async ({ name, version: version2, bundleDigest }) => {
|
|
55923
|
+
try {
|
|
55924
|
+
const { CloudExecutionClient: CloudExecutionClient2 } = await Promise.resolve().then(() => (init_cloud_executions(), exports_cloud_executions));
|
|
55925
|
+
return mcpJson(await (await CloudExecutionClient2.configured()).eligibility(name, version2, bundleDigest));
|
|
55926
|
+
} catch (error61) {
|
|
55927
|
+
return mcpError("CLOUD_ELIGIBILITY_FAILED", error61.message);
|
|
55928
|
+
}
|
|
55929
|
+
});
|
|
55776
55930
|
server.registerTool("scaffold_skill", {
|
|
55777
55931
|
title: "Scaffold Skill",
|
|
55778
55932
|
description: "Create a portable skill folder under ~/.hasna/skills/installed/<name> with SKILL.md, skill.json, AGENTS.md, package.json, and src/index.ts.",
|