@hasna/skills 0.8.12 → 0.9.2
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 -5
- package/bin/index.js +271 -19
- package/bin/mcp.js +248 -154
- 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.js +122 -13
- package/dist/lib/agent-integration.d.ts +26 -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 +1301 -502
- 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/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/README.md
CHANGED
|
@@ -271,6 +271,15 @@ Vendor file restoration uses an exclusive hard link to preserve concurrent files
|
|
|
271
271
|
Do not retry an interrupted operation until its journal and both paths have been
|
|
272
272
|
reconciled; a failed final journal write may leave the earlier durable intent.
|
|
273
273
|
|
|
274
|
+
For a reviewed migration that must retire one exact native copy, pass
|
|
275
|
+
`--target-manifest <file>`. The JSON manifest uses schema
|
|
276
|
+
`hasna.skills-native-migration-targets.v1` and binds each target to an agent,
|
|
277
|
+
an existing absolute `projectRoot`, a normalized project-relative `path`, and
|
|
278
|
+
the complete directory `treeSha256`. Exact-target mode refuses broad selector
|
|
279
|
+
flags, protected or vendor entries, missing or ambiguous paths, symlinks,
|
|
280
|
+
digest drift, and writes a manifest digest into the recovery journal. Every
|
|
281
|
+
inventory entry outside the manifest remains untouched.
|
|
282
|
+
|
|
274
283
|
`skills hook agents --json` reports the supported adapters and coverage limits.
|
|
275
284
|
Claude and Codex have lifecycle context hooks; Gemini uses `BeforeAgent`, and
|
|
276
285
|
OpenCode uses its awaited message plugin. Cursor receives selected context at
|
|
@@ -592,13 +601,20 @@ before submitting again. The selected profile must include this exact version,
|
|
|
592
601
|
and the consumer needs `runs:write` as well as `skills:read`.
|
|
593
602
|
|
|
594
603
|
Cloud execution is enabled only when the deployment configures a reviewed image
|
|
595
|
-
and exact bundle allowlist
|
|
596
|
-
uploaded code is not admitted.
|
|
604
|
+
and exact bundle allowlist; arbitrary
|
|
605
|
+
uploaded code is not admitted. The PDF lane accepts reviewed `pdf-generate` versions.
|
|
606
|
+
The optional `regex-test.v1` pure contract accepts only a tenant-specific, reviewed
|
|
607
|
+
self-contained Bun bundle, with no secrets, dependencies or network. Uploading or
|
|
608
|
+
selecting a bundle never admits it to cloud execution. Check an exact version with
|
|
609
|
+
`skills executions eligibility <skill> --skill-version <version> --json`; this
|
|
610
|
+
reads metadata without creating a run. Runs capture version, bundle digest, input digest,
|
|
597
611
|
runtime image digest, limits and policy. The cloud worker runs in a separate
|
|
598
612
|
Fargate task; the skill process has no API/provider credentials, no network, a
|
|
599
613
|
read-only root and bounded temporary storage, execution time and output.
|
|
600
614
|
`GET /skills/v1/capabilities` reports whether this deployment has cloud execution
|
|
601
|
-
configured
|
|
615
|
+
configured and lists supported `cloudExecutionContracts`. Each exact bundle needs
|
|
616
|
+
its own review and admission; the flag does not promise arbitrary executable
|
|
617
|
+
support. Authorization and runtime availability are checked separately.
|
|
602
618
|
|
|
603
619
|
On a managed station, local execution also resolves the selected immutable
|
|
604
620
|
bundle. Self-contained local executables run with explicit environment references
|
|
@@ -751,7 +767,7 @@ of app folders, and `XDG_CONFIG_HOME` is not consulted at all.
|
|
|
751
767
|
| `skills sessions show <id> --json` | | Inspect one session's exact profile revision and receipt hash without loading payloads |
|
|
752
768
|
| `skills sessions reconcile <id> --from-profile <id> --from-revision <rev> --receipt-sha256 <sha> --selection-profile <id> --profile-revision <rev>` | | Plan an explicit migration of one live session; `--apply --plan-digest <digest> --plan-issued-at <time> --plan-expires-at <time>` (within five minutes) preserves its old receipt and applies the reviewed replacement |
|
|
753
769
|
| `skills hook agents --json` | | Report maintained adapters and explicit coverage limits |
|
|
754
|
-
| `skills migrate native` | | Inventory native copies; `--apply` archives managed copies, with explicit `--include-unmanaged
|
|
770
|
+
| `skills migrate native` | | Inventory native copies; `--apply` archives managed copies, with explicit `--include-unmanaged`, `--include-vendor`, or reviewed `--target-manifest` selection |
|
|
755
771
|
| `skills pull --all --selection-profile <id>` | | With CLI loading active, refresh the selected profile into the verified cache |
|
|
756
772
|
| `skills sync --selection-profile <id> [--check] [--station <id>]` | | Sync or check the selected profile/cache; optionally record station state |
|
|
757
773
|
| `skills station-state <id>` | | Read a station's sync receipt in the authenticated workspace |
|
|
@@ -1433,7 +1449,7 @@ src/
|
|
|
1433
1449
|
|---|---|---|
|
|
1434
1450
|
| Catalog skills | 0 | `SKILLS.length` (`src/lib/registry-data/`) |
|
|
1435
1451
|
| Categories | 17 | `CATEGORIES` (`src/lib/registry-types.ts`) |
|
|
1436
|
-
| MCP tools |
|
|
1452
|
+
| MCP tools | 82 | `tools/list` against a live `buildServer()` |
|
|
1437
1453
|
|
|
1438
1454
|
Every number in this table is re-derived from the source tree on each test run by
|
|
1439
1455
|
`src/lib/readme-derived-counts.test.ts`, so a drifted figure fails a test rather
|
package/bin/index.js
CHANGED
|
@@ -36868,7 +36868,7 @@ var package_default;
|
|
|
36868
36868
|
var init_package = __esm(() => {
|
|
36869
36869
|
package_default = {
|
|
36870
36870
|
name: "@hasna/skills",
|
|
36871
|
-
version: "0.
|
|
36871
|
+
version: "0.9.2",
|
|
36872
36872
|
description: "Skills library for AI coding agents",
|
|
36873
36873
|
type: "module",
|
|
36874
36874
|
bin: {
|
|
@@ -39186,6 +39186,7 @@ function readManifest(skillPath, fallbackName, normalizeName) {
|
|
|
39186
39186
|
const version = readDeclaredSkillVersion(skillPath) ?? PORTABLE_SKILL_DEFAULT_VERSION;
|
|
39187
39187
|
const kind = parseSkillKind(stringField(jsonManifest, "kind") ?? frontmatter?.kind);
|
|
39188
39188
|
const commands = parseManifestCommands(jsonManifest) ?? (kind === "instruction" ? [] : inferPackageCommands(pkg, name)) ?? [];
|
|
39189
|
+
const runtime = parseManifestRuntime(jsonManifest) ?? inferRuntimeFromCommands(commands);
|
|
39189
39190
|
return {
|
|
39190
39191
|
$schema: stringField(jsonManifest, "$schema") ?? PORTABLE_SKILL_SCHEMA,
|
|
39191
39192
|
standard: stringField(jsonManifest, "standard") ?? PORTABLE_SKILL_STANDARD,
|
|
@@ -39198,7 +39199,7 @@ function readManifest(skillPath, fallbackName, normalizeName) {
|
|
|
39198
39199
|
...kind ? { kind } : {},
|
|
39199
39200
|
inputs: kind === "instruction" ? parseManifestInputs(jsonManifest) ?? [] : parseManifestInputs(jsonManifest) ?? DEFAULT_INPUTS,
|
|
39200
39201
|
commands,
|
|
39201
|
-
...
|
|
39202
|
+
...runtime ? { runtime } : {},
|
|
39202
39203
|
...parseManifestProvenance(jsonManifest) ? { provenance: parseManifestProvenance(jsonManifest) } : {}
|
|
39203
39204
|
};
|
|
39204
39205
|
}
|
|
@@ -39717,6 +39718,12 @@ function inferPackageCommands(pkg, fallbackName) {
|
|
|
39717
39718
|
}
|
|
39718
39719
|
return;
|
|
39719
39720
|
}
|
|
39721
|
+
function inferRuntimeFromCommands(commands) {
|
|
39722
|
+
const entrypoint = commands.find((command) => command.entry)?.entry;
|
|
39723
|
+
if (!entrypoint || !entrypoint.toLowerCase().endsWith(".py"))
|
|
39724
|
+
return;
|
|
39725
|
+
return { ...defaultRuntimeContract(entrypoint), runtime: "python3" };
|
|
39726
|
+
}
|
|
39720
39727
|
function readJsonObject(path) {
|
|
39721
39728
|
const parsed = JSON.parse(readFileSync6(path, "utf-8"));
|
|
39722
39729
|
if (!isRecord2(parsed))
|
|
@@ -55351,6 +55358,82 @@ function treeHash(root2) {
|
|
|
55351
55358
|
visit2(root2);
|
|
55352
55359
|
return hash2.digest("hex");
|
|
55353
55360
|
}
|
|
55361
|
+
function parseNativeMigrationTargetManifest(input) {
|
|
55362
|
+
const digest2 = sha(input);
|
|
55363
|
+
let value;
|
|
55364
|
+
try {
|
|
55365
|
+
value = JSON.parse(input.toString());
|
|
55366
|
+
} catch {
|
|
55367
|
+
throw new Error("Invalid native migration target manifest JSON");
|
|
55368
|
+
}
|
|
55369
|
+
if (!value || typeof value !== "object" || Array.isArray(value))
|
|
55370
|
+
throw new Error("Invalid native migration target manifest");
|
|
55371
|
+
const record2 = value;
|
|
55372
|
+
if (record2.schema !== "hasna.skills-native-migration-targets.v1" || Object.keys(record2).some((key) => !["schema", "targets"].includes(key)))
|
|
55373
|
+
throw new Error("Unsupported native migration target manifest schema");
|
|
55374
|
+
if (!Array.isArray(record2.targets) || record2.targets.length === 0 || record2.targets.length > 1000)
|
|
55375
|
+
throw new Error("Native migration target manifest must contain 1 to 1000 targets");
|
|
55376
|
+
const targets = [];
|
|
55377
|
+
const identities = new Set;
|
|
55378
|
+
for (const target of record2.targets) {
|
|
55379
|
+
if (!target || typeof target !== "object" || Array.isArray(target))
|
|
55380
|
+
throw new Error("Invalid native migration target");
|
|
55381
|
+
const item = target;
|
|
55382
|
+
if (Object.keys(item).some((key) => !["agent", "projectRoot", "path", "treeSha256"].includes(key)) || typeof item.agent !== "string" || typeof item.projectRoot !== "string" || typeof item.path !== "string" || typeof item.treeSha256 !== "string")
|
|
55383
|
+
throw new Error("Invalid native migration target fields");
|
|
55384
|
+
if (!INTEGRATION_AGENTS.includes(item.agent))
|
|
55385
|
+
throw new Error(`Unsupported native migration target agent: ${item.agent}`);
|
|
55386
|
+
const projectRoot = resolve16(item.projectRoot);
|
|
55387
|
+
if (!isAbsolute15(item.projectRoot) || projectRoot !== item.projectRoot || !existsSync15(projectRoot) || !lstatSync18(projectRoot).isDirectory())
|
|
55388
|
+
throw new Error(`Native migration target project root must be an existing absolute directory: ${item.projectRoot}`);
|
|
55389
|
+
assertSafePath(projectRoot);
|
|
55390
|
+
if (isAbsolute15(item.path) || item.path.includes("\\") || item.path.length === 0 || item.path.includes("\x00"))
|
|
55391
|
+
throw new Error(`Native migration target path must be normalized and project-relative: ${item.path}`);
|
|
55392
|
+
const resolved = resolve16(projectRoot, item.path), normalized = relative5(projectRoot, resolved);
|
|
55393
|
+
if (!normalized || normalized === ".." || normalized.startsWith(`..${sep6}`) || isAbsolute15(normalized) || normalized !== item.path)
|
|
55394
|
+
throw new Error(`Native migration target path escapes or is not normalized: ${item.path}`);
|
|
55395
|
+
if (!/^[0-9a-f]{64}$/.test(item.treeSha256))
|
|
55396
|
+
throw new Error(`Invalid native migration tree digest for ${item.path}`);
|
|
55397
|
+
const identity4 = `${item.agent}\x00${projectRoot}\x00${normalized}`;
|
|
55398
|
+
if (identities.has(identity4))
|
|
55399
|
+
throw new Error(`Duplicate native migration target: ${item.path}`);
|
|
55400
|
+
identities.add(identity4);
|
|
55401
|
+
targets.push({ agent: item.agent, projectRoot, path: normalized, treeSha256: item.treeSha256 });
|
|
55402
|
+
}
|
|
55403
|
+
return { schema: "hasna.skills-native-migration-targets.v1", targets, digest: digest2 };
|
|
55404
|
+
}
|
|
55405
|
+
function readNativeMigrationTargetManifest(path) {
|
|
55406
|
+
return parseNativeMigrationTargetManifest(readNativeBytes(path, 4 * 1024 * 1024));
|
|
55407
|
+
}
|
|
55408
|
+
function selectNativeMigrationTargets(inventory, manifest) {
|
|
55409
|
+
if (manifest.schema !== "hasna.skills-native-migration-targets.v1" || !/^[0-9a-f]{64}$/.test(manifest.digest) || !Array.isArray(manifest.targets) || manifest.targets.length === 0)
|
|
55410
|
+
throw new Error("Invalid native migration target manifest");
|
|
55411
|
+
const identities = new Set;
|
|
55412
|
+
const selected = [];
|
|
55413
|
+
for (const target of manifest.targets) {
|
|
55414
|
+
const projectRoot = resolve16(target.projectRoot), normalized = relative5(projectRoot, resolve16(projectRoot, target.path));
|
|
55415
|
+
if (!isAbsolute15(target.projectRoot) || projectRoot !== target.projectRoot || !existsSync15(projectRoot) || !lstatSync18(projectRoot).isDirectory())
|
|
55416
|
+
throw new Error(`Native migration target project root must be an existing absolute directory: ${target.projectRoot}`);
|
|
55417
|
+
assertSafePath(projectRoot);
|
|
55418
|
+
if (isAbsolute15(target.path) || target.path.includes("\\") || target.path.includes("\x00") || !normalized || normalized === ".." || normalized.startsWith(`..${sep6}`) || normalized !== target.path || !/^[0-9a-f]{64}$/.test(target.treeSha256))
|
|
55419
|
+
throw new Error(`Invalid native migration target: ${target.path}`);
|
|
55420
|
+
const identity4 = `${target.agent}\x00${projectRoot}\x00${normalized}`;
|
|
55421
|
+
if (identities.has(identity4))
|
|
55422
|
+
throw new Error(`Duplicate native migration target: ${target.path}`);
|
|
55423
|
+
identities.add(identity4);
|
|
55424
|
+
const expected = resolve16(target.projectRoot, target.path);
|
|
55425
|
+
const matches2 = inventory.filter((entry2) => entry2.agent === target.agent && resolve16(entry2.path) === expected);
|
|
55426
|
+
if (matches2.length !== 1)
|
|
55427
|
+
throw new Error(`Native migration target was not found exactly once: ${target.agent} ${target.projectRoot}/${target.path}`);
|
|
55428
|
+
const entry = matches2[0];
|
|
55429
|
+
if (entry.bridge || entry.system || entry.vendor)
|
|
55430
|
+
throw new Error(`Native migration target is protected or vendor-owned: ${entry.path}`);
|
|
55431
|
+
if (entry.hash !== target.treeSha256 || treeHash(entry.path) !== target.treeSha256)
|
|
55432
|
+
throw new Error(`Native migration target changed after review: ${entry.path}`);
|
|
55433
|
+
selected.push(entry);
|
|
55434
|
+
}
|
|
55435
|
+
return selected;
|
|
55436
|
+
}
|
|
55354
55437
|
function projectAncestorDirectories(projects) {
|
|
55355
55438
|
const directories = new Set;
|
|
55356
55439
|
for (const project of projects) {
|
|
@@ -55839,12 +55922,13 @@ function archiveNativeSkills(inventory, options) {
|
|
|
55839
55922
|
if (!expected || treeHash(entry.path) !== entry.hash || !isOwnedCliBridge(entry.path, [expected]))
|
|
55840
55923
|
throw new Error(`Skills bridge changed after planning: ${entry.path}`);
|
|
55841
55924
|
}
|
|
55842
|
-
const selected = inventory.filter((entry) => !entry.bridge && !entry.system && (entry.vendor ? options.includeVendor : entry.managed || options.includeUnmanaged));
|
|
55925
|
+
const selected = options.targetManifest ? selectNativeMigrationTargets(inventory, options.targetManifest) : inventory.filter((entry) => !entry.bridge && !entry.system && (entry.vendor ? options.includeVendor : entry.managed || options.includeUnmanaged));
|
|
55843
55926
|
for (const entry of selected)
|
|
55844
55927
|
if (treeHash(entry.path) !== entry.hash)
|
|
55845
55928
|
throw new Error(`Native skill changed after planning: ${entry.path}`);
|
|
55929
|
+
const targetManifest = options.targetManifest ? { schema: options.targetManifest.schema, digest: options.targetManifest.digest, targetCount: options.targetManifest.targets.length } : undefined;
|
|
55846
55930
|
if (!selected.length)
|
|
55847
|
-
return { entries: [], ...aliases.length ? { rootAliases: aliases } : {} };
|
|
55931
|
+
return { entries: [], ...targetManifest ? { targetManifest } : {}, ...aliases.length ? { rootAliases: aliases } : {} };
|
|
55848
55932
|
const operationId = randomUUID6(), archiveRoot = join29(options.dataDir ?? getDataDir(), "migration", operationId, "native"), receiptPath = join29(archiveRoot, "receipt.json");
|
|
55849
55933
|
const entries = selected.map((entry, index) => ({
|
|
55850
55934
|
source: entry.vendor ? join29(entry.path, "SKILL.md") : entry.path,
|
|
@@ -55853,7 +55937,7 @@ function archiveNativeSkills(inventory, options) {
|
|
|
55853
55937
|
...entry.vendor ? { discoveryOnly: true } : {},
|
|
55854
55938
|
status: "planned"
|
|
55855
55939
|
}));
|
|
55856
|
-
const journal = { version: 2, operationId, status: "planned", entries, ...aliases.length ? { rootAliases: aliases } : {} };
|
|
55940
|
+
const journal = { version: 2, operationId, status: "planned", entries, ...targetManifest ? { targetManifest } : {}, ...aliases.length ? { rootAliases: aliases } : {} };
|
|
55857
55941
|
const moved = [];
|
|
55858
55942
|
const verifyArchive = (entry) => {
|
|
55859
55943
|
assertSafePath(entry.archive);
|
|
@@ -55947,7 +56031,7 @@ function archiveNativeSkills(inventory, options) {
|
|
|
55947
56031
|
} catch {}
|
|
55948
56032
|
throw new Error(`Native archive failed; inspect the recovery journal at ${receiptPath} before retrying.`, { cause: error });
|
|
55949
56033
|
}
|
|
55950
|
-
return { entries: entries.map(({ status, conflict, ...entry }) => entry), receiptPath, ...aliases.length ? { rootAliases: aliases } : {} };
|
|
56034
|
+
return { entries: entries.map(({ status, conflict, ...entry }) => entry), receiptPath, ...targetManifest ? { targetManifest } : {}, ...aliases.length ? { rootAliases: aliases } : {} };
|
|
55951
56035
|
}
|
|
55952
56036
|
function assertManagedAgentBridge(agent, options = {}) {
|
|
55953
56037
|
const home = resolve16(options.home ?? homedir4()), dataDir3 = options.dataDir ?? getDataDirReadOnly();
|
|
@@ -76206,6 +76290,8 @@ function normalizeRemoteSkill(skill) {
|
|
|
76206
76290
|
tags: skill.tags || ["remote"],
|
|
76207
76291
|
dependencies: skill.dependencies,
|
|
76208
76292
|
...skill.version ? { version: skill.version } : {},
|
|
76293
|
+
...skill.revisionId !== undefined ? { revisionId: skill.revisionId } : {},
|
|
76294
|
+
...skill.lifecycle ? { lifecycle: skill.lifecycle } : {},
|
|
76209
76295
|
availability: normalizeRemoteAvailability(skill.availability),
|
|
76210
76296
|
source: "remote"
|
|
76211
76297
|
};
|
|
@@ -76323,6 +76409,8 @@ var init_remote_registry = __esm(() => {
|
|
|
76323
76409
|
tags: exports_external.array(exports_external.string()).optional(),
|
|
76324
76410
|
dependencies: exports_external.array(exports_external.string()).optional(),
|
|
76325
76411
|
version: exports_external.string().optional(),
|
|
76412
|
+
revisionId: exports_external.union([exports_external.string().regex(/^[\x21-\x7e]+$/), exports_external.null()]).optional(),
|
|
76413
|
+
lifecycle: exports_external.enum(["active", "archived"]).optional(),
|
|
76326
76414
|
availability: remoteAvailabilitySchema.optional()
|
|
76327
76415
|
}).passthrough().refine((skill) => skill.name || skill.slug, {
|
|
76328
76416
|
message: "Remote skill requires name or slug"
|
|
@@ -77711,6 +77799,7 @@ __export(exports_remote_client, {
|
|
|
77711
77799
|
RemoteWorkspaceSelectionError: () => RemoteWorkspaceSelectionError,
|
|
77712
77800
|
RemoteWorkspaceMemberError: () => RemoteWorkspaceMemberError,
|
|
77713
77801
|
RemoteSkillsClient: () => RemoteSkillsClient,
|
|
77802
|
+
RemoteSkillLifecycleError: () => RemoteSkillLifecycleError,
|
|
77714
77803
|
RemoteRouteUnsupportedError: () => RemoteRouteUnsupportedError,
|
|
77715
77804
|
RemoteRequestError: () => RemoteRequestError,
|
|
77716
77805
|
RemoteQuoteUnavailableError: () => RemoteQuoteUnavailableError,
|
|
@@ -77842,6 +77931,39 @@ class RemoteSkillsClient {
|
|
|
77842
77931
|
})();
|
|
77843
77932
|
return this.capabilities;
|
|
77844
77933
|
}
|
|
77934
|
+
async addSkillPublishScope(keyId, expectedScopes, expectedOrgId) {
|
|
77935
|
+
if (!/^[A-Za-z0-9_-]+$/.test(keyId))
|
|
77936
|
+
throw new Error("Invalid API key id");
|
|
77937
|
+
if (!/^[A-Za-z0-9_-]{1,256}$/.test(expectedOrgId))
|
|
77938
|
+
throw new Error("Invalid expected organization id");
|
|
77939
|
+
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)
|
|
77940
|
+
throw new Error("Invalid expected API key scopes");
|
|
77941
|
+
const path = `/api/v1/admin/keys/${encodeURIComponent(keyId)}/scopes`;
|
|
77942
|
+
const response = await this.request(path, { method: "PATCH", body: JSON.stringify({ expected_scopes: expectedScopes, add_scopes: ["skills:publish"] }) });
|
|
77943
|
+
if (!response.ok)
|
|
77944
|
+
throw new RemoteRequestError(path, response.status);
|
|
77945
|
+
let value;
|
|
77946
|
+
try {
|
|
77947
|
+
value = JSON.parse(new TextDecoder("utf-8", { fatal: true }).decode(await readBoundedResponse(response, 64 * 1024)));
|
|
77948
|
+
} catch {
|
|
77949
|
+
throw new Error("Invalid API key scope update response");
|
|
77950
|
+
}
|
|
77951
|
+
if (!value || typeof value !== "object" || Array.isArray(value))
|
|
77952
|
+
throw new Error("Invalid API key scope update response");
|
|
77953
|
+
const body = value;
|
|
77954
|
+
const returnedId = body.keyId;
|
|
77955
|
+
const orgId = body.orgId;
|
|
77956
|
+
const scopes = body.scopes;
|
|
77957
|
+
if (returnedId !== keyId || typeof orgId !== "string" || !orgId || expectedOrgId !== undefined && orgId !== expectedOrgId || !Array.isArray(scopes) || scopes.some((scope) => typeof scope !== "string") || body.updated !== true) {
|
|
77958
|
+
throw new Error("Invalid API key scope update response");
|
|
77959
|
+
}
|
|
77960
|
+
const returnedScopes = scopes;
|
|
77961
|
+
const expectedResult = expectedScopes.includes("skills:publish") ? expectedScopes : [...expectedScopes, "skills:publish"];
|
|
77962
|
+
if (returnedScopes.length !== expectedResult.length || expectedResult.some((scope, index) => returnedScopes[index] !== scope)) {
|
|
77963
|
+
throw new Error("API key scope update response did not preserve the expected scopes");
|
|
77964
|
+
}
|
|
77965
|
+
return { keyId, orgId, scopes: [...returnedScopes], updated: true };
|
|
77966
|
+
}
|
|
77845
77967
|
async submitQuotedRun(slug2, input2 = {}, args = [], approval = {}) {
|
|
77846
77968
|
runQuoteReceipt(approval.quoteReceipt);
|
|
77847
77969
|
({ input: input2, args, approval } = JSON.parse(JSON.stringify({ input: input2, args, approval })));
|
|
@@ -78359,6 +78481,19 @@ class RemoteSkillsClient {
|
|
|
78359
78481
|
async deleteSkill(slug2) {
|
|
78360
78482
|
return this.request(`/api/v1/skills/${encodeURIComponent(slug2)}`, { method: "DELETE" });
|
|
78361
78483
|
}
|
|
78484
|
+
async setSkillLifecycle(slug2, lifecycle, options) {
|
|
78485
|
+
const path = `/api/v1/skills/${encodeURIComponent(slug2)}/lifecycle`;
|
|
78486
|
+
const response = await this.request(path, {
|
|
78487
|
+
method: "PATCH",
|
|
78488
|
+
headers: { "If-Match": options.expectedRevisionId },
|
|
78489
|
+
body: JSON.stringify({ lifecycle, ...options.reason ? { reason: options.reason } : {}, ...options.replacementSlug ? { replacementSlug: options.replacementSlug } : {} })
|
|
78490
|
+
});
|
|
78491
|
+
if (!response.ok) {
|
|
78492
|
+
const code = await readResponseCode(response);
|
|
78493
|
+
throw new RemoteSkillLifecycleError(path, response.status, code);
|
|
78494
|
+
}
|
|
78495
|
+
return response.json();
|
|
78496
|
+
}
|
|
78362
78497
|
async downloadSkillBundle(slug2) {
|
|
78363
78498
|
return this.request(`/api/v1/skills/${encodeURIComponent(slug2)}/bundle`, { method: "GET" });
|
|
78364
78499
|
}
|
|
@@ -78510,9 +78645,13 @@ function normalizeSkillSummaryList(payload) {
|
|
|
78510
78645
|
return payload.map(normalizeSkillSummary);
|
|
78511
78646
|
}
|
|
78512
78647
|
async function responseBodyCarriesCode(response, codes) {
|
|
78648
|
+
const code = await readResponseCode(response);
|
|
78649
|
+
return code !== undefined && codes.includes(code);
|
|
78650
|
+
}
|
|
78651
|
+
async function readResponseCode(response) {
|
|
78513
78652
|
const reader = response.body?.getReader();
|
|
78514
78653
|
if (!reader)
|
|
78515
|
-
return
|
|
78654
|
+
return;
|
|
78516
78655
|
const maximum = 8 * 1024;
|
|
78517
78656
|
let deadline;
|
|
78518
78657
|
const expired = new Promise((_, reject2) => {
|
|
@@ -78527,7 +78666,7 @@ async function responseBodyCarriesCode(response, codes) {
|
|
|
78527
78666
|
break;
|
|
78528
78667
|
size2 += next.value.byteLength;
|
|
78529
78668
|
if (size2 > maximum)
|
|
78530
|
-
return
|
|
78669
|
+
return;
|
|
78531
78670
|
chunks.push(next.value);
|
|
78532
78671
|
}
|
|
78533
78672
|
const bytes = new Uint8Array(size2);
|
|
@@ -78538,11 +78677,11 @@ async function responseBodyCarriesCode(response, codes) {
|
|
|
78538
78677
|
}
|
|
78539
78678
|
const payload = JSON.parse(new TextDecoder("utf-8", { fatal: true }).decode(bytes));
|
|
78540
78679
|
if (!payload || typeof payload !== "object" || Array.isArray(payload) || !Object.hasOwn(payload, "code"))
|
|
78541
|
-
return
|
|
78680
|
+
return;
|
|
78542
78681
|
const code = payload.code;
|
|
78543
|
-
return typeof code === "string" &&
|
|
78682
|
+
return typeof code === "string" && /^[A-Z][A-Z0-9_:-]{0,127}$/.test(code) ? code : undefined;
|
|
78544
78683
|
} catch {
|
|
78545
|
-
return
|
|
78684
|
+
return;
|
|
78546
78685
|
} finally {
|
|
78547
78686
|
clearTimeout(deadline);
|
|
78548
78687
|
reader.cancel().catch(() => {});
|
|
@@ -78571,7 +78710,7 @@ async function createRemoteSkillsClient(env3 = process.env) {
|
|
|
78571
78710
|
function createRemoteSkillsClientReadOnly(env3 = process.env) {
|
|
78572
78711
|
return createRemoteSkillsClient(env3);
|
|
78573
78712
|
}
|
|
78574
|
-
var RemoteRouteUnsupportedError, RemoteRequestError, RemoteCreditCheckoutError, RemoteQuoteUnavailableError, RemoteWorkspaceMemberError, RemoteWorkspaceSelectionError, RemoteCapabilityUnavailableError, INVALID_SKILL_VERSION_RESPONSE = "Remote skill version payload did not match the expected contract.";
|
|
78713
|
+
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.";
|
|
78575
78714
|
var init_remote_client = __esm(() => {
|
|
78576
78715
|
init_remote_recurring();
|
|
78577
78716
|
init_remote_invitations();
|
|
@@ -78608,6 +78747,17 @@ var init_remote_client = __esm(() => {
|
|
|
78608
78747
|
this.name = "RemoteRequestError";
|
|
78609
78748
|
}
|
|
78610
78749
|
};
|
|
78750
|
+
KNOWN_SKILL_LIFECYCLE_CODES = new Set(["SKILL_ARCHIVE_PROFILE_CONFLICT", "LIFECYCLE_ROLE_REQUIRED"]);
|
|
78751
|
+
RemoteSkillLifecycleError = class RemoteSkillLifecycleError extends RemoteRequestError {
|
|
78752
|
+
code;
|
|
78753
|
+
constructor(path, status, code) {
|
|
78754
|
+
super(path, status);
|
|
78755
|
+
this.name = "RemoteSkillLifecycleError";
|
|
78756
|
+
const safeCode = code !== undefined && KNOWN_SKILL_LIFECYCLE_CODES.has(code) ? code : undefined;
|
|
78757
|
+
this.code = safeCode;
|
|
78758
|
+
this.message = `Skill lifecycle update was refused (HTTP ${status}${safeCode ? `, code ${safeCode}` : ""})`;
|
|
78759
|
+
}
|
|
78760
|
+
};
|
|
78611
78761
|
RemoteCreditCheckoutError = class RemoteCreditCheckoutError extends RemoteRequestError {
|
|
78612
78762
|
code;
|
|
78613
78763
|
requestIdempotencyKey;
|
|
@@ -78663,7 +78813,7 @@ var init_remote_client = __esm(() => {
|
|
|
78663
78813
|
// src/lib/revision.ts
|
|
78664
78814
|
import { createHash as createHash16 } from "crypto";
|
|
78665
78815
|
function revisionIdOf(content) {
|
|
78666
|
-
const
|
|
78816
|
+
const canonicalContent = {
|
|
78667
78817
|
slug: content.slug,
|
|
78668
78818
|
displayName: content.displayName,
|
|
78669
78819
|
description: content.description,
|
|
@@ -78675,7 +78825,13 @@ function revisionIdOf(content) {
|
|
|
78675
78825
|
skillMd: content.skillMd ?? null,
|
|
78676
78826
|
bundleSha256: content.bundleSha256 ?? null,
|
|
78677
78827
|
bundleByteSize: content.bundleByteSize ?? null
|
|
78678
|
-
}
|
|
78828
|
+
};
|
|
78829
|
+
if (content.lifecycle === "archived" || content.archiveReason !== undefined || content.replacementSlug !== undefined) {
|
|
78830
|
+
canonicalContent.lifecycle = content.lifecycle ?? "active";
|
|
78831
|
+
canonicalContent.archiveReason = content.archiveReason ?? null;
|
|
78832
|
+
canonicalContent.replacementSlug = content.replacementSlug ?? null;
|
|
78833
|
+
}
|
|
78834
|
+
const canonical4 = JSON.stringify(canonicalContent);
|
|
78679
78835
|
return createHash16("sha256").update(canonical4).digest("hex");
|
|
78680
78836
|
}
|
|
78681
78837
|
var REVISION_ID_PATTERN;
|
|
@@ -82131,6 +82287,15 @@ class CloudExecutionClient {
|
|
|
82131
82287
|
}
|
|
82132
82288
|
return response;
|
|
82133
82289
|
}
|
|
82290
|
+
async eligibility(slug2, version2, bundleDigest) {
|
|
82291
|
+
if (!/^[a-z0-9-]+$/.test(slug2) || !/^\d+\.\d+\.\d+$/.test(version2) || bundleDigest !== undefined && !/^(sha256:)?[a-f0-9]{64}$/.test(bundleDigest))
|
|
82292
|
+
throw Error("Cloud eligibility requires an exact skill version and valid digest");
|
|
82293
|
+
const query = new URLSearchParams({ version: version2, ...bundleDigest ? { bundleDigest } : {} });
|
|
82294
|
+
const v = await jsonBody(await this.request(`${slug2}/eligibility?${query}`));
|
|
82295
|
+
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)))
|
|
82296
|
+
throw Error("Invalid cloud eligibility receipt");
|
|
82297
|
+
return v;
|
|
82298
|
+
}
|
|
82134
82299
|
async submit(slug2, version2, input2, key, selection) {
|
|
82135
82300
|
if (!/^[a-z0-9-]+$/.test(slug2) || !/^\d+\.\d+\.\d+$/.test(version2))
|
|
82136
82301
|
throw Error("Cloud execution requires skill@exact-version");
|
|
@@ -103202,6 +103367,18 @@ var init_run_routing = __esm(() => {
|
|
|
103202
103367
|
import { existsSync as existsSync28, readdirSync as readdirSync17, statSync as statSync16 } from "fs";
|
|
103203
103368
|
import { join as join46 } from "path";
|
|
103204
103369
|
function registerOperationTools(server) {
|
|
103370
|
+
server.registerTool("cloud_skill_eligibility", {
|
|
103371
|
+
title: "Cloud Skill Eligibility",
|
|
103372
|
+
description: "Inspect the authenticated workspace's exact reviewed cloud contract. Does not execute or admit a skill.",
|
|
103373
|
+
inputSchema: { name: exports_external.string(), version: exports_external.string(), bundleDigest: exports_external.string().optional() }
|
|
103374
|
+
}, async ({ name, version: version2, bundleDigest }) => {
|
|
103375
|
+
try {
|
|
103376
|
+
const { CloudExecutionClient: CloudExecutionClient2 } = await Promise.resolve().then(() => (init_cloud_executions(), exports_cloud_executions));
|
|
103377
|
+
return mcpJson(await (await CloudExecutionClient2.configured()).eligibility(name, version2, bundleDigest));
|
|
103378
|
+
} catch (error61) {
|
|
103379
|
+
return mcpError("CLOUD_ELIGIBILITY_FAILED", error61.message);
|
|
103380
|
+
}
|
|
103381
|
+
});
|
|
103205
103382
|
server.registerTool("scaffold_skill", {
|
|
103206
103383
|
title: "Scaffold Skill",
|
|
103207
103384
|
description: "Create a portable skill folder under ~/.hasna/skills/installed/<name> with SKILL.md, skill.json, AGENTS.md, package.json, and src/index.ts.",
|
|
@@ -105518,6 +105695,15 @@ function registerRuntime(parent) {
|
|
|
105518
105695
|
await handleRun(name, args2, options, separatorBeforeSkill);
|
|
105519
105696
|
});
|
|
105520
105697
|
const executions = parent.command("executions").description("Inspect isolated cloud executions");
|
|
105698
|
+
executions.command("eligibility").argument("<skill>").requiredOption("--skill-version <version>", "Exact published version").option("--bundle-digest <digest>", "Expected published bundle SHA256").option("--json", "Output as JSON", false).description("Inspect reviewed cloud eligibility without executing a skill").action(async (slug2, options) => {
|
|
105699
|
+
try {
|
|
105700
|
+
const client2 = await CloudExecutionClient.configured();
|
|
105701
|
+
await writeCliOutput(JSON.stringify(await client2.eligibility(slug2, options.skillVersion, options.bundleDigest), null, 2));
|
|
105702
|
+
} catch (error61) {
|
|
105703
|
+
console.error(JSON.stringify({ error: error61.message }));
|
|
105704
|
+
process.exitCode = 1;
|
|
105705
|
+
}
|
|
105706
|
+
});
|
|
105521
105707
|
for (const operation of ["show", "logs", "artifacts", "cancel"]) {
|
|
105522
105708
|
executions.command(operation).argument("<execution-id>").option("--json", "Output as JSON", false).action(async (id2) => {
|
|
105523
105709
|
try {
|
|
@@ -108432,11 +108618,16 @@ function registerAgentIntegration(parent) {
|
|
|
108432
108618
|
}
|
|
108433
108619
|
});
|
|
108434
108620
|
const migrate = parent.command("migrate").description("Preserve and retire native agent skill copies");
|
|
108435
|
-
migrate.command("native").option("--project <directory>", "Also inventory a project and its ancestors (the current directory and its ancestors are always included)").option("--include-unmanaged", "Archive user-authored skills as well as Skills-managed copies", false).option("--include-vendor", "Retire vendor SKILL.md discovery files while preserving plugin scripts and assets", false).option("--discovery-inputs <file>", "Advanced reviewed active plugin roots and source hashes").option("--allow-root-aliases", "Allow home .claude/.codex aliases to existing directories within this home", false).option("--apply", "Move selected skills to private archives outside agent discovery roots", false).option("--json", "Output inventory and archive receipt as JSON", false).description("Inventory native skill copies; preserve complete directories before retiring them").action(async (options) => {
|
|
108621
|
+
migrate.command("native").option("--project <directory>", "Also inventory a project and its ancestors (the current directory and its ancestors are always included)").option("--include-unmanaged", "Archive user-authored skills as well as Skills-managed copies", false).option("--include-vendor", "Retire vendor SKILL.md discovery files while preserving plugin scripts and assets", false).option("--target-manifest <file>", "Reviewed exact native migration target manifest").option("--discovery-inputs <file>", "Advanced reviewed active plugin roots and source hashes").option("--allow-root-aliases", "Allow home .claude/.codex aliases to existing directories within this home", false).option("--apply", "Move selected skills to private archives outside agent discovery roots", false).option("--json", "Output inventory and archive receipt as JSON", false).description("Inventory native skill copies; preserve complete directories before retiring them").action(async (options) => {
|
|
108436
108622
|
try {
|
|
108437
108623
|
const discoveryInputs = options.discoveryInputs ? JSON.parse(readFileSync37(options.discoveryInputs, "utf8")) : undefined;
|
|
108624
|
+
if (options.targetManifest && (options.includeUnmanaged || options.includeVendor))
|
|
108625
|
+
throw new Error("--target-manifest cannot be combined with broad native migration selectors");
|
|
108626
|
+
const targetManifest = options.targetManifest ? readNativeMigrationTargetManifest(options.targetManifest) : undefined;
|
|
108438
108627
|
const inventory = inventoryNativeSkills(undefined, { projectDirs: [process.cwd(), ...options.project ? [options.project] : []], includeVendor: options.includeVendor, configured: options.includeVendor, discoveryInputs, allowRootAliases: options.allowRootAliases });
|
|
108439
|
-
|
|
108628
|
+
if (targetManifest)
|
|
108629
|
+
selectNativeMigrationTargets(inventory, targetManifest);
|
|
108630
|
+
const result2 = options.apply ? archiveNativeSkills(inventory, { includeUnmanaged: options.includeUnmanaged, includeVendor: options.includeVendor, allowRootAliases: options.allowRootAliases, targetManifest }) : { entries: [], ...targetManifest ? { targetManifest: { schema: targetManifest.schema, digest: targetManifest.digest, targetCount: targetManifest.targets.length } } : {} };
|
|
108440
108631
|
if (options.json)
|
|
108441
108632
|
await writeCliOutput(JSON.stringify({ applied: options.apply, inventory, ...result2 }));
|
|
108442
108633
|
else
|
|
@@ -109219,6 +109410,7 @@ function validateRawCandidate(candidate2) {
|
|
|
109219
109410
|
const issues = validatePortableManifestContract(candidate2, { strict: true });
|
|
109220
109411
|
if (issues.length)
|
|
109221
109412
|
throw new Error(`Invalid skill.json: ${issues.map((issue2) => issue2.code).join(", ")}`);
|
|
109413
|
+
validateRuntimeEntrypointCompatibility(candidate2);
|
|
109222
109414
|
for (const field2 of ["inputs", "commands"]) {
|
|
109223
109415
|
const items = candidate2[field2];
|
|
109224
109416
|
if (items === undefined)
|
|
@@ -109244,6 +109436,18 @@ function validateRawCandidate(candidate2) {
|
|
|
109244
109436
|
}
|
|
109245
109437
|
}
|
|
109246
109438
|
}
|
|
109439
|
+
function validateRuntimeEntrypointCompatibility(candidate2) {
|
|
109440
|
+
if (candidate2.kind !== "executable" || !isRecord6(candidate2.runtime))
|
|
109441
|
+
return;
|
|
109442
|
+
const runtime = candidate2.runtime.runtime;
|
|
109443
|
+
const entrypoint = candidate2.runtime.entrypoint;
|
|
109444
|
+
if (typeof runtime !== "string" || typeof entrypoint !== "string")
|
|
109445
|
+
return;
|
|
109446
|
+
const extension = entrypoint.toLowerCase().split(".").pop();
|
|
109447
|
+
if (extension === "py" && runtime !== "python3") {
|
|
109448
|
+
throw new Error("Invalid skill.json: runtime.entrypoint with .py extension requires runtime python3.");
|
|
109449
|
+
}
|
|
109450
|
+
}
|
|
109247
109451
|
function compareVersions(left, right) {
|
|
109248
109452
|
const parts = (value) => value.split("+")[0].split(/-(.*)/s).slice(0, 2);
|
|
109249
109453
|
const [a, ap] = parts(left), [b, bp] = parts(right);
|
|
@@ -109779,7 +109983,8 @@ var exports_registry2 = {};
|
|
|
109779
109983
|
__export(exports_registry2, {
|
|
109780
109984
|
registerVersions: () => registerVersions,
|
|
109781
109985
|
registerRegistry: () => registerRegistry,
|
|
109782
|
-
registerPull: () => registerPull
|
|
109986
|
+
registerPull: () => registerPull,
|
|
109987
|
+
registerLifecycle: () => registerLifecycle
|
|
109783
109988
|
});
|
|
109784
109989
|
function registerRegistry(parent) {
|
|
109785
109990
|
const registry2 = parent.command("registry").description("Generate registry artifacts for hosted skills services");
|
|
@@ -109935,6 +110140,25 @@ Versions of ${name}
|
|
|
109935
110140
|
}
|
|
109936
110141
|
});
|
|
109937
110142
|
}
|
|
110143
|
+
function registerLifecycle(parent) {
|
|
110144
|
+
parent.command("lifecycle").argument("<name>", "Skill name on the configured instance").requiredOption("--state <state>", "Lifecycle state: active or archived").requiredOption("--revision <revision>", "Exact revision id from skills info/get").option("--reason <reason>", "Bounded archive reason").option("--replacement <slug>", "Optional successor slug").option("--json", "Output as JSON", false).description("Change a hosted skill's active/archive lifecycle with an exact revision guard").action(async (name, options) => {
|
|
110145
|
+
if (options.state !== "active" && options.state !== "archived")
|
|
110146
|
+
throw new Error("--state must be active or archived");
|
|
110147
|
+
const client2 = await createRemoteSkillsClient();
|
|
110148
|
+
if (!client2)
|
|
110149
|
+
throw new Error("No API key configured, so there is no instance to update.");
|
|
110150
|
+
try {
|
|
110151
|
+
const result2 = await client2.setSkillLifecycle(name.trim(), options.state, { expectedRevisionId: options.revision, ...options.reason ? { reason: options.reason } : {}, ...options.replacement ? { replacementSlug: options.replacement } : {} });
|
|
110152
|
+
console.log(options.json ? JSON.stringify(result2, null, 2) : `Updated ${name} to ${options.state}.`);
|
|
110153
|
+
} catch (error61) {
|
|
110154
|
+
if (options.json)
|
|
110155
|
+
console.log(JSON.stringify({ error: error61.message }, null, 2));
|
|
110156
|
+
else
|
|
110157
|
+
console.error(source_default.red(error61.message));
|
|
110158
|
+
process.exitCode = 1;
|
|
110159
|
+
}
|
|
110160
|
+
});
|
|
110161
|
+
}
|
|
109938
110162
|
var init_registry2 = __esm(() => {
|
|
109939
110163
|
init_source();
|
|
109940
110164
|
init_registry_sync();
|
|
@@ -110482,6 +110706,11 @@ function authIdentityPayload(authSource, live, cached2, offline = false) {
|
|
|
110482
110706
|
const userId = stringField2(user?.id) ?? cached2?.userId;
|
|
110483
110707
|
const orgId = stringField2(organization2?.id) ?? cached2?.orgId;
|
|
110484
110708
|
const role3 = stringField2(user?.role);
|
|
110709
|
+
const apiKey = recordField(root2.apiKey) ?? recordField(data?.apiKey);
|
|
110710
|
+
const apiKeyId = stringField2(apiKey?.id);
|
|
110711
|
+
const apiKeyScopes = apiKey?.scopes;
|
|
110712
|
+
const safeApiKeyId = apiKeyId && /^[A-Za-z0-9_-]{1,256}$/.test(apiKeyId) ? apiKeyId : undefined;
|
|
110713
|
+
const safeApiKeyScopes = Array.isArray(apiKeyScopes) && apiKeyScopes.length <= 32 && apiKeyScopes.every((scope) => typeof scope === "string" && scope.length <= 128 && /^(?:\*|[a-z][a-z0-9_-]*:(?:\*|[a-z][a-z0-9_-]*))$/.test(scope)) ? [...new Set(apiKeyScopes)] : undefined;
|
|
110485
110714
|
return {
|
|
110486
110715
|
status: "authenticated",
|
|
110487
110716
|
authSource,
|
|
@@ -110491,7 +110720,8 @@ function authIdentityPayload(authSource, live, cached2, offline = false) {
|
|
|
110491
110720
|
...orgName ? { organizationName: orgName } : {},
|
|
110492
110721
|
...userId ? { userId } : {},
|
|
110493
110722
|
...orgId ? { orgId } : {},
|
|
110494
|
-
...role3 ? { role: role3 } : {}
|
|
110723
|
+
...role3 ? { role: role3 } : {},
|
|
110724
|
+
...safeApiKeyId && safeApiKeyScopes ? { apiKeyId: safeApiKeyId, scopes: safeApiKeyScopes } : {}
|
|
110495
110725
|
};
|
|
110496
110726
|
}
|
|
110497
110727
|
function printWhoami(payload) {
|
|
@@ -110501,6 +110731,8 @@ function printWhoami(payload) {
|
|
|
110501
110731
|
console.log(source_default.bold("Org: ") + payload.organization);
|
|
110502
110732
|
if (payload.role)
|
|
110503
110733
|
console.log(source_default.bold("Role: ") + payload.role);
|
|
110734
|
+
if (payload.apiKeyId)
|
|
110735
|
+
console.log(source_default.bold("Key: ") + payload.apiKeyId);
|
|
110504
110736
|
if (payload.organizationName)
|
|
110505
110737
|
console.log(source_default.bold("Name: ") + payload.organizationName);
|
|
110506
110738
|
if (payload.authSource)
|
|
@@ -110822,6 +111054,25 @@ function registerAuth(parent) {
|
|
|
110822
111054
|
writeCommandError(error61, "Failed to revoke API key", options.json);
|
|
110823
111055
|
}
|
|
110824
111056
|
});
|
|
111057
|
+
keys3.command("add-publish-scope").argument("<key-id>").requiredOption("--expected-scopes <csv>", "Current comma-separated scopes; stale values are refused").option("--json", "Output as JSON", false).description("Add skills:publish to one existing key without minting or rotating it").action(async (id3, options) => {
|
|
111058
|
+
try {
|
|
111059
|
+
const connection = await resolveSkillsConnection();
|
|
111060
|
+
if (!connection)
|
|
111061
|
+
throw new Error("A hosted Skills credential is required for key scope administration");
|
|
111062
|
+
const identity4 = await apiRequest("/api/auth/whoami", { headers: { Authorization: `Bearer ${connection.apiKey}` } }, connection.apiOrigin);
|
|
111063
|
+
const identityOrg = recordField(identity4.organization);
|
|
111064
|
+
const orgId = stringField2(identityOrg?.id);
|
|
111065
|
+
if (!orgId)
|
|
111066
|
+
throw new Error("The Skills authority did not return a tenant identity; refusing key scope administration");
|
|
111067
|
+
const result2 = await new RemoteSkillsClient(connection.apiKey, connection.apiOrigin).addSkillPublishScope(id3, options.expectedScopes.split(",").map((scope) => scope.trim()).filter(Boolean), orgId);
|
|
111068
|
+
if (options.json || !isTTY)
|
|
111069
|
+
console.log(JSON.stringify(result2, null, 2));
|
|
111070
|
+
else
|
|
111071
|
+
console.log(source_default.green(`Added skills:publish to ${id3}; scopes read back from the server.`));
|
|
111072
|
+
} catch (error61) {
|
|
111073
|
+
writeCommandError(error61, "Failed to add Skills publication scope", options.json);
|
|
111074
|
+
}
|
|
111075
|
+
});
|
|
110825
111076
|
auth.command("login").description("Sign in with browser/device code or email code").option("--email <email>", "Email address (non-interactive)").option("--code <code>", "Verification code (non-interactive)").option("--membership-id <id>", "Enroll an exact workspace membership into an explicit HASNA_PROFILE").option("--code-stdin", "Read a fresh six-digit code for workspace enrollment from stdin").option("--api-key <key>", "Verify and store an API key").option("--device", "Use browser/device-code login", false).option("--no-open", "Do not open a browser for device-code login").option("--poll", "Poll until browser authentication completes in non-interactive mode", false).option("--poll-timeout-ms <ms>", "Maximum time to wait for device-code login").option("--json", "Output result as JSON", false).action(async (options) => {
|
|
110826
111077
|
if (options.membershipId !== undefined) {
|
|
110827
111078
|
if (options.apiKey || options.device || options.code || options.poll) {
|
|
@@ -119885,10 +120136,11 @@ var { registerPortableSkillCommands: registerPortableSkillCommands2 } = await Pr
|
|
|
119885
120136
|
registerPortableSkillCommands2(program2);
|
|
119886
120137
|
var { registerSchedule: registerSchedule2 } = await Promise.resolve().then(() => (init_schedule(), exports_schedule));
|
|
119887
120138
|
registerSchedule2(program2);
|
|
119888
|
-
var { registerRegistry: registerRegistry2, registerPull: registerPull2, registerVersions: registerVersions2 } = await Promise.resolve().then(() => (init_registry2(), exports_registry2));
|
|
120139
|
+
var { registerRegistry: registerRegistry2, registerPull: registerPull2, registerVersions: registerVersions2, registerLifecycle: registerLifecycle2 } = await Promise.resolve().then(() => (init_registry2(), exports_registry2));
|
|
119889
120140
|
registerRegistry2(program2);
|
|
119890
120141
|
registerPull2(program2);
|
|
119891
120142
|
registerVersions2(program2);
|
|
120143
|
+
registerLifecycle2(program2);
|
|
119892
120144
|
var { registerPublish: registerPublish2 } = await Promise.resolve().then(() => (init_publish(), exports_publish));
|
|
119893
120145
|
registerPublish2(program2);
|
|
119894
120146
|
var { registerPrivatePublications: registerPrivatePublications2 } = await Promise.resolve().then(() => (init_private_publications(), exports_private_publications));
|