@dot-skill/runtime 0.4.2 → 0.5.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 +36 -0
- package/dist/index.d.ts +39 -3
- package/dist/index.js +228 -22
- package/package.json +20 -5
package/README.md
ADDED
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
# `@dot-skill/runtime`
|
|
2
|
+
|
|
3
|
+
Reference runtime for the [Open `.skill` Protocol](https://github.com/dot-skill/dot-skill).
|
|
4
|
+
|
|
5
|
+
Load a `.skill` archive, verify integrity and trust profile, resolve inputs, and run in **inspect**, **dry_run**, or **execute** modes. Prefer inspect and dry-run before execute.
|
|
6
|
+
|
|
7
|
+
## Install
|
|
8
|
+
|
|
9
|
+
```bash
|
|
10
|
+
npm i @dot-skill/runtime
|
|
11
|
+
```
|
|
12
|
+
|
|
13
|
+
## Lifecycle
|
|
14
|
+
|
|
15
|
+
`LoadAndVerify` → `NegotiateCapabilities` → `ResolveInputs` → `Consent` → `Execute` → `Verify` → `EmitSkillRun`
|
|
16
|
+
|
|
17
|
+
Modes: `inspect` · `explain` · `dry_run` · `execute` · `resume`
|
|
18
|
+
|
|
19
|
+
Fails clearly when required capabilities or minted trust profiles are unmet. Does not silently degrade.
|
|
20
|
+
|
|
21
|
+
## Trust
|
|
22
|
+
|
|
23
|
+
- Digests and seals are visible without executing workflow steps.
|
|
24
|
+
- Trust profiles: `open` | `minted` | `anchored` | `issuer:<id>`
|
|
25
|
+
- Reference mint verification matches `@dot-skill/core` (dev HMAC ≠ production PKI)
|
|
26
|
+
|
|
27
|
+
## Related
|
|
28
|
+
|
|
29
|
+
- [`@dot-skill/core`](https://www.npmjs.com/package/@dot-skill/core) — pack / validate / mint
|
|
30
|
+
- [`skillerr`](https://www.npmjs.com/package/skillerr) — `skill inspect` / `skill run`
|
|
31
|
+
|
|
32
|
+
Docs: [RUNTIME.md](https://github.com/dot-skill/dot-skill/blob/main/docs/RUNTIME.md) · [SECURITY.md](https://github.com/dot-skill/dot-skill/blob/main/docs/SECURITY.md)
|
|
33
|
+
|
|
34
|
+
## License
|
|
35
|
+
|
|
36
|
+
MIT
|
package/dist/index.d.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import type { CapabilityAdapterHint, CapabilityRequirement, InputSlot, RuntimeMode, SkillPackageFiles, SkillRun, TrustProfile } from "@dot-skill/protocol";
|
|
2
|
-
import { inspectSkill, unpackSkill, validatePackageBytes } from "@dot-skill/core";
|
|
2
|
+
import { inspectSkill, inspectTrustView, unpackSkill, validatePackageBytes } from "@dot-skill/core";
|
|
3
3
|
export interface CapabilityAdapter {
|
|
4
4
|
name: string;
|
|
5
5
|
supports: (cap: CapabilityRequirement) => boolean;
|
|
@@ -16,7 +16,29 @@ export interface RuntimeHost {
|
|
|
16
16
|
title: string;
|
|
17
17
|
permissions: string[];
|
|
18
18
|
steps: string[];
|
|
19
|
-
}) => Promise<
|
|
19
|
+
}) => Promise<{
|
|
20
|
+
allowed: boolean;
|
|
21
|
+
actor: string;
|
|
22
|
+
at: string;
|
|
23
|
+
}>;
|
|
24
|
+
decide?: (decision: {
|
|
25
|
+
id: string;
|
|
26
|
+
prompt: string;
|
|
27
|
+
choices?: string[];
|
|
28
|
+
}) => Promise<{
|
|
29
|
+
decision: string;
|
|
30
|
+
actor: string;
|
|
31
|
+
at: string;
|
|
32
|
+
}>;
|
|
33
|
+
verifyAssertion?: (assertion: {
|
|
34
|
+
id: string;
|
|
35
|
+
assertion: string;
|
|
36
|
+
check: "runtime" | "capability" | "human" | "agent";
|
|
37
|
+
evidence?: string[];
|
|
38
|
+
}) => Promise<{
|
|
39
|
+
passed: boolean;
|
|
40
|
+
detail?: string;
|
|
41
|
+
}>;
|
|
20
42
|
resolveSecret?: (ref: string) => Promise<string>;
|
|
21
43
|
env?: Record<string, string>;
|
|
22
44
|
adapters?: CapabilityAdapter[];
|
|
@@ -32,7 +54,21 @@ export interface RunOptions {
|
|
|
32
54
|
trust_profile?: TrustProfile;
|
|
33
55
|
/** Reference verifier only; production runtimes should use a real trust store. */
|
|
34
56
|
issuer_secret?: string;
|
|
57
|
+
/**
|
|
58
|
+
* Explicit unsafe opt-in to execute unsigned / development / self_reported packages.
|
|
59
|
+
* Required for execute when TrustView is not verified_issuer.
|
|
60
|
+
*/
|
|
61
|
+
allow_untrusted?: boolean;
|
|
62
|
+
/** Allow public-dev HMAC seals for local testing only. */
|
|
63
|
+
allow_development_issuer?: boolean;
|
|
35
64
|
}
|
|
65
|
+
/**
|
|
66
|
+
* Deny-by-default: refuse undeclared network / filesystem / secret adapter use.
|
|
67
|
+
* Fail closed when consent is required but missing.
|
|
68
|
+
*/
|
|
69
|
+
export declare function assertCapabilityAllowed(pkg: SkillPackageFiles, cap: CapabilityRequirement, args?: Record<string, unknown>): void;
|
|
70
|
+
/** Resolve a secret ref only if the input slot declares secret sensitivity/source. */
|
|
71
|
+
export declare function resolveDeclaredSecret(pkg: SkillPackageFiles, slotName: string, host: RuntimeHost): Promise<string>;
|
|
36
72
|
export declare function explainPackage(pkg: SkillPackageFiles): {
|
|
37
73
|
title: string;
|
|
38
74
|
description: string;
|
|
@@ -47,4 +83,4 @@ export declare function explainPackage(pkg: SkillPackageFiles): {
|
|
|
47
83
|
};
|
|
48
84
|
export declare function runSkillPackage(pkg: SkillPackageFiles, host?: RuntimeHost, options?: RunOptions): Promise<SkillRun>;
|
|
49
85
|
export declare function runSkillArchive(archive: Uint8Array, host?: RuntimeHost, options?: RunOptions): Promise<SkillRun>;
|
|
50
|
-
export { inspectSkill, validatePackageBytes, unpackSkill, explainPackage as explain };
|
|
86
|
+
export { inspectSkill, inspectTrustView, validatePackageBytes, unpackSkill, explainPackage as explain, };
|
package/dist/index.js
CHANGED
|
@@ -1,7 +1,90 @@
|
|
|
1
1
|
import { randomUUID } from "node:crypto";
|
|
2
|
-
import {
|
|
3
|
-
|
|
4
|
-
|
|
2
|
+
import { readFileSync } from "node:fs";
|
|
3
|
+
import { inspectSkill, inspectTrustView, unpackSkill, validatePackageBytes, verifyMintTrust, sha256Digest, } from "@dot-skill/core";
|
|
4
|
+
function loadRuntimeIdentity() {
|
|
5
|
+
const metadata = JSON.parse(readFileSync(new URL("../package.json", import.meta.url), "utf8"));
|
|
6
|
+
if (typeof metadata.name !== "string" || typeof metadata.version !== "string") {
|
|
7
|
+
throw new Error("Invalid @dot-skill/runtime package metadata");
|
|
8
|
+
}
|
|
9
|
+
return { name: metadata.name, version: metadata.version };
|
|
10
|
+
}
|
|
11
|
+
const { name: RUNTIME_NAME, version: RUNTIME_VERSION } = loadRuntimeIdentity();
|
|
12
|
+
function permissionCovers(permissions, sideEffect) {
|
|
13
|
+
return permissions.find((p) => p.side_effect_class === sideEffect);
|
|
14
|
+
}
|
|
15
|
+
/**
|
|
16
|
+
* Deny-by-default: refuse undeclared network / filesystem / secret adapter use.
|
|
17
|
+
* Fail closed when consent is required but missing.
|
|
18
|
+
*/
|
|
19
|
+
export function assertCapabilityAllowed(pkg, cap, args = {}) {
|
|
20
|
+
const policy = pkg.manifest.policy;
|
|
21
|
+
const side = cap.side_effect_class;
|
|
22
|
+
if (side === "network") {
|
|
23
|
+
if (!policy.allow_network) {
|
|
24
|
+
throw new Error(`Denied: capability ${cap.name} requires network but policy.allow_network=false (deny-by-default)`);
|
|
25
|
+
}
|
|
26
|
+
const perm = permissionCovers(pkg.manifest.permissions, "network");
|
|
27
|
+
if (!perm) {
|
|
28
|
+
throw new Error(`Denied: capability ${cap.name} uses network but no network permission is declared`);
|
|
29
|
+
}
|
|
30
|
+
const hostArg = typeof args.host === "string"
|
|
31
|
+
? args.host
|
|
32
|
+
: typeof args.url === "string"
|
|
33
|
+
? args.url
|
|
34
|
+
: typeof args.endpoint === "string"
|
|
35
|
+
? args.endpoint
|
|
36
|
+
: undefined;
|
|
37
|
+
if (perm.hosts?.length && hostArg) {
|
|
38
|
+
const ok = perm.hosts.some((h) => hostArg === h || hostArg.includes(h) || hostArg.startsWith(h));
|
|
39
|
+
if (!ok) {
|
|
40
|
+
throw new Error(`Denied: network host/url not in declared permission.hosts for ${cap.name}`);
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
if (side === "read" || side === "write" || side === "destructive") {
|
|
45
|
+
const perm = permissionCovers(pkg.manifest.permissions, side);
|
|
46
|
+
if (!perm && side !== "read") {
|
|
47
|
+
throw new Error(`Denied: capability ${cap.name} uses ${side} but no matching permission is declared`);
|
|
48
|
+
}
|
|
49
|
+
const pathArg = typeof args.path === "string"
|
|
50
|
+
? args.path
|
|
51
|
+
: typeof args.file === "string"
|
|
52
|
+
? args.file
|
|
53
|
+
: typeof args.root === "string"
|
|
54
|
+
? args.root
|
|
55
|
+
: undefined;
|
|
56
|
+
const roots = policy.filesystem_roots;
|
|
57
|
+
if (pathArg && roots?.length) {
|
|
58
|
+
const ok = roots.some((root) => pathArg === root || pathArg.startsWith(root.endsWith("/") ? root : `${root}/`));
|
|
59
|
+
if (!ok) {
|
|
60
|
+
throw new Error(`Denied: path ${pathArg} outside policy.filesystem_roots for ${cap.name}`);
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
if (pathArg && perm?.paths?.length) {
|
|
64
|
+
const ok = perm.paths.some((p) => pathArg === p || pathArg.startsWith(p.endsWith("/") ? p : `${p}/`));
|
|
65
|
+
if (!ok) {
|
|
66
|
+
throw new Error(`Denied: path ${pathArg} not in declared permission.paths for ${cap.name}`);
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
if (side === "none") {
|
|
71
|
+
// no side effects — allowed
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
function assertSecretAccessAllowed(pkg, slotName) {
|
|
75
|
+
const slot = pkg.manifest.inputs.find((i) => i.name === slotName);
|
|
76
|
+
if (!slot || (slot.sensitivity !== "secret" && slot.source !== "secret")) {
|
|
77
|
+
throw new Error(`Denied: secret access for undeclared slot ${slotName} (deny-by-default)`);
|
|
78
|
+
}
|
|
79
|
+
}
|
|
80
|
+
/** Resolve a secret ref only if the input slot declares secret sensitivity/source. */
|
|
81
|
+
export async function resolveDeclaredSecret(pkg, slotName, host) {
|
|
82
|
+
assertSecretAccessAllowed(pkg, slotName);
|
|
83
|
+
if (!host.resolveSecret) {
|
|
84
|
+
throw new Error("Denied: no resolveSecret callback (fail closed for secrets)");
|
|
85
|
+
}
|
|
86
|
+
return host.resolveSecret(slotName);
|
|
87
|
+
}
|
|
5
88
|
function substitute(template, inputs) {
|
|
6
89
|
return template.replace(/\{\{\s*([a-zA-Z_][a-zA-Z0-9_]*)\s*\}\}/g, (_, name) => {
|
|
7
90
|
const v = inputs[name];
|
|
@@ -119,16 +202,28 @@ export async function runSkillPackage(pkg, host = {}, options = {}) {
|
|
|
119
202
|
error: `Missing required inputs: ${missing.map((m) => m.name).join(", ")}`,
|
|
120
203
|
};
|
|
121
204
|
}
|
|
122
|
-
const consentNeeded = pkg.manifest.permissions
|
|
205
|
+
const consentNeeded = new Set(pkg.manifest.permissions
|
|
123
206
|
.filter((p) => p.requires_consent)
|
|
124
|
-
.map((p) => p.side_effect_class);
|
|
125
|
-
|
|
126
|
-
|
|
207
|
+
.map((p) => p.side_effect_class));
|
|
208
|
+
for (const side of pkg.manifest.policy.consent_for ?? []) {
|
|
209
|
+
if (pkg.manifest.capabilities.some((c) => c.side_effect_class === side) ||
|
|
210
|
+
pkg.manifest.permissions.some((p) => p.side_effect_class === side)) {
|
|
211
|
+
consentNeeded.add(side);
|
|
212
|
+
}
|
|
213
|
+
}
|
|
214
|
+
if (mode === "execute" && consentNeeded.size) {
|
|
215
|
+
if (!host.consent) {
|
|
216
|
+
return failRun(runId, pkg, mode, resolved, secret_refs, stepRecords, verifications, started, host, "Explicit permission consent required; runtime host has no authenticated consent callback (fail closed)");
|
|
217
|
+
}
|
|
218
|
+
const consent = await host.consent({
|
|
127
219
|
title: pkg.manifest.title,
|
|
128
|
-
permissions: consentNeeded,
|
|
220
|
+
permissions: [...consentNeeded],
|
|
129
221
|
steps: pkg.workflow.steps.map((s) => `${s.id}:${s.kind}`),
|
|
130
222
|
});
|
|
131
|
-
if (!
|
|
223
|
+
if (!consent.actor || !consent.at) {
|
|
224
|
+
return failRun(runId, pkg, mode, resolved, secret_refs, stepRecords, verifications, started, host, "Permission consent callback returned invalid actor/timestamp evidence");
|
|
225
|
+
}
|
|
226
|
+
if (!consent.allowed) {
|
|
132
227
|
return {
|
|
133
228
|
kind: "skill_run",
|
|
134
229
|
id: runId,
|
|
@@ -382,6 +477,12 @@ async function executeStep(step, ctx) {
|
|
|
382
477
|
const cap = ctx.pkg.manifest.capabilities.find((c) => c.name === step.capability);
|
|
383
478
|
if (!cap)
|
|
384
479
|
throw new Error(`Unknown capability ${step.capability}`);
|
|
480
|
+
const args = { ...(step.arguments ?? {}) };
|
|
481
|
+
for (const [k, bind] of Object.entries(step.argument_bindings ?? {})) {
|
|
482
|
+
args[k] = ctx.inputs[bind] ?? ctx.stepOutputs[bind];
|
|
483
|
+
}
|
|
484
|
+
// Deny-by-default capability gate (even in dry_run for visibility of refusals).
|
|
485
|
+
assertCapabilityAllowed(ctx.pkg, cap, args);
|
|
385
486
|
if (ctx.dry) {
|
|
386
487
|
return {
|
|
387
488
|
output: { dry_run: true, capability: cap.name, arguments: step.arguments },
|
|
@@ -399,10 +500,6 @@ async function executeStep(step, ctx) {
|
|
|
399
500
|
}
|
|
400
501
|
throw new Error(`No adapter for capability ${cap.name}`);
|
|
401
502
|
}
|
|
402
|
-
const args = { ...(step.arguments ?? {}) };
|
|
403
|
-
for (const [k, bind] of Object.entries(step.argument_bindings ?? {})) {
|
|
404
|
-
args[k] = ctx.inputs[bind] ?? ctx.stepOutputs[bind];
|
|
405
|
-
}
|
|
406
503
|
const inv = await adapter.invoke(cap, args);
|
|
407
504
|
if (!inv.ok)
|
|
408
505
|
throw new Error(inv.error ?? "tool failed");
|
|
@@ -464,11 +561,18 @@ async function executeStep(step, ctx) {
|
|
|
464
561
|
resultAs: step.result_as,
|
|
465
562
|
};
|
|
466
563
|
}
|
|
467
|
-
|
|
468
|
-
|
|
469
|
-
|
|
564
|
+
if (!ctx.host.decide) {
|
|
565
|
+
throw new Error(`human_decision ${step.id} unsupported: runtime requires an authenticated decide callback; input values cannot spoof human approval`);
|
|
566
|
+
}
|
|
567
|
+
const evidence = await ctx.host.decide({
|
|
568
|
+
id: step.id,
|
|
569
|
+
prompt: step.prompt,
|
|
570
|
+
choices: step.choices,
|
|
571
|
+
});
|
|
572
|
+
if (!evidence.actor || !evidence.at || !evidence.decision) {
|
|
573
|
+
throw new Error(`human_decision ${step.id} returned invalid approval evidence`);
|
|
470
574
|
}
|
|
471
|
-
|
|
575
|
+
return { output: evidence, resultAs: step.result_as };
|
|
472
576
|
}
|
|
473
577
|
case "verify": {
|
|
474
578
|
const results = [];
|
|
@@ -498,6 +602,34 @@ async function executeStep(step, ctx) {
|
|
|
498
602
|
const val = ctx.stepOutputs[key] ?? ctx.inputs[key];
|
|
499
603
|
results.push({ assertion, passed: val !== undefined });
|
|
500
604
|
}
|
|
605
|
+
else if (assertion.startsWith("precondition:") ||
|
|
606
|
+
assertion.startsWith("contract_assertion:")) {
|
|
607
|
+
const id = assertion.slice(assertion.indexOf(":") + 1);
|
|
608
|
+
const definition = ctx.pkg.workflow.preconditions?.status === "specified"
|
|
609
|
+
? ctx.pkg.workflow.preconditions.items.find((item) => item.id === id)
|
|
610
|
+
: ctx.pkg.workflow.verification?.status === "specified"
|
|
611
|
+
? ctx.pkg.workflow.verification.items.find((item) => item.id === id)
|
|
612
|
+
: undefined;
|
|
613
|
+
if (!definition) {
|
|
614
|
+
results.push({ assertion, passed: false, detail: "Contract assertion missing" });
|
|
615
|
+
}
|
|
616
|
+
else if (!ctx.host.verifyAssertion) {
|
|
617
|
+
results.push({
|
|
618
|
+
assertion,
|
|
619
|
+
passed: false,
|
|
620
|
+
detail: "Unsupported domain assertion: runtime host must provide verifyAssertion; presence is not proof",
|
|
621
|
+
});
|
|
622
|
+
}
|
|
623
|
+
else {
|
|
624
|
+
const checked = await ctx.host.verifyAssertion({
|
|
625
|
+
id: definition.id,
|
|
626
|
+
assertion: "assertion" in definition ? definition.assertion : "",
|
|
627
|
+
check: definition.check,
|
|
628
|
+
evidence: "evidence" in definition ? definition.evidence : undefined,
|
|
629
|
+
});
|
|
630
|
+
results.push({ assertion, ...checked });
|
|
631
|
+
}
|
|
632
|
+
}
|
|
501
633
|
else {
|
|
502
634
|
results.push({
|
|
503
635
|
assertion,
|
|
@@ -529,8 +661,9 @@ async function executeStep(step, ctx) {
|
|
|
529
661
|
}
|
|
530
662
|
function evalWhen(expr, inputs, stepOutputs) {
|
|
531
663
|
const m = expr.match(/^input:([a-zA-Z0-9_]+)(?:==(.*))?$/);
|
|
532
|
-
if (!m)
|
|
533
|
-
|
|
664
|
+
if (!m) {
|
|
665
|
+
throw new Error(`Unsupported branch expression ${JSON.stringify(expr)}; supported form is input:name or input:name==value`);
|
|
666
|
+
}
|
|
534
667
|
const val = inputs[m[1]] ?? stepOutputs[m[1]];
|
|
535
668
|
if (m[2] === undefined)
|
|
536
669
|
return Boolean(val);
|
|
@@ -563,6 +696,42 @@ export async function runSkillArchive(archive, host = {}, options = {}) {
|
|
|
563
696
|
}
|
|
564
697
|
const unpacked = unpackSkill(archive);
|
|
565
698
|
const manifest = unpacked.manifest;
|
|
699
|
+
const mode = options.mode ?? "execute";
|
|
700
|
+
const trustView = inspectTrustView(archive);
|
|
701
|
+
// Execute refuses unsigned / development / self_reported unless explicit unsafe flag.
|
|
702
|
+
if (mode === "execute" || mode === "resume") {
|
|
703
|
+
const trusted = trustView.trust_state === "verified_issuer";
|
|
704
|
+
if (!trusted && !options.allow_untrusted) {
|
|
705
|
+
const started = new Date().toISOString();
|
|
706
|
+
return {
|
|
707
|
+
kind: "skill_run",
|
|
708
|
+
id: `run_${randomUUID().replace(/-/g, "").slice(0, 16)}`,
|
|
709
|
+
skill_id: manifest.id,
|
|
710
|
+
skill_version: manifest.version,
|
|
711
|
+
package_digest: manifest.package_digest,
|
|
712
|
+
status: "failed",
|
|
713
|
+
mode,
|
|
714
|
+
resolved_inputs: {},
|
|
715
|
+
steps: [],
|
|
716
|
+
verifications: [
|
|
717
|
+
{
|
|
718
|
+
assertion: "trust_gate",
|
|
719
|
+
passed: false,
|
|
720
|
+
detail: `${trustView.label}. Pass allow_untrusted / --allow-untrusted to execute anyway.`,
|
|
721
|
+
},
|
|
722
|
+
],
|
|
723
|
+
runtime: {
|
|
724
|
+
name: RUNTIME_NAME,
|
|
725
|
+
version: RUNTIME_VERSION,
|
|
726
|
+
host: host.host,
|
|
727
|
+
model: host.model,
|
|
728
|
+
},
|
|
729
|
+
started_at: started,
|
|
730
|
+
finished_at: new Date().toISOString(),
|
|
731
|
+
error: `Refusing execute: ${trustView.label}`,
|
|
732
|
+
};
|
|
733
|
+
}
|
|
734
|
+
}
|
|
566
735
|
const trustProfile = options.trust_profile ??
|
|
567
736
|
(manifest.policy.require_anchor
|
|
568
737
|
? "anchored"
|
|
@@ -570,7 +739,13 @@ export async function runSkillArchive(archive, host = {}, options = {}) {
|
|
|
570
739
|
? "minted"
|
|
571
740
|
: manifest.policy.trust_profile ?? "open");
|
|
572
741
|
if (trustProfile !== "open") {
|
|
573
|
-
const
|
|
742
|
+
const nonExecute = mode === "dry_run" || mode === "inspect" || mode === "explain";
|
|
743
|
+
const trust = verifyMintTrust(archive, trustProfile, {
|
|
744
|
+
issuer_secret: options.issuer_secret,
|
|
745
|
+
allow_development_issuer: options.allow_development_issuer ??
|
|
746
|
+
(options.allow_untrusted === true || nonExecute),
|
|
747
|
+
allow_self_reported: options.allow_untrusted === true || nonExecute,
|
|
748
|
+
});
|
|
574
749
|
if (!trust.ok) {
|
|
575
750
|
const started = new Date().toISOString();
|
|
576
751
|
return {
|
|
@@ -580,7 +755,7 @@ export async function runSkillArchive(archive, host = {}, options = {}) {
|
|
|
580
755
|
skill_version: manifest.version,
|
|
581
756
|
package_digest: manifest.package_digest,
|
|
582
757
|
status: "failed",
|
|
583
|
-
mode
|
|
758
|
+
mode,
|
|
584
759
|
resolved_inputs: {},
|
|
585
760
|
steps: [],
|
|
586
761
|
verifications: trust.issues.map((issue) => ({
|
|
@@ -600,6 +775,37 @@ export async function runSkillArchive(archive, host = {}, options = {}) {
|
|
|
600
775
|
};
|
|
601
776
|
}
|
|
602
777
|
}
|
|
778
|
+
else if ((mode === "execute" || mode === "resume") && !options.allow_untrusted) {
|
|
779
|
+
// Open profile packages are explicitly untrusted for execute.
|
|
780
|
+
const started = new Date().toISOString();
|
|
781
|
+
return {
|
|
782
|
+
kind: "skill_run",
|
|
783
|
+
id: `run_${randomUUID().replace(/-/g, "").slice(0, 16)}`,
|
|
784
|
+
skill_id: manifest.id,
|
|
785
|
+
skill_version: manifest.version,
|
|
786
|
+
package_digest: manifest.package_digest,
|
|
787
|
+
status: "failed",
|
|
788
|
+
mode,
|
|
789
|
+
resolved_inputs: {},
|
|
790
|
+
steps: [],
|
|
791
|
+
verifications: [
|
|
792
|
+
{
|
|
793
|
+
assertion: "open_untrusted",
|
|
794
|
+
passed: false,
|
|
795
|
+
detail: "Open/unsigned packages require --allow-untrusted for execute",
|
|
796
|
+
},
|
|
797
|
+
],
|
|
798
|
+
runtime: {
|
|
799
|
+
name: RUNTIME_NAME,
|
|
800
|
+
version: RUNTIME_VERSION,
|
|
801
|
+
host: host.host,
|
|
802
|
+
model: host.model,
|
|
803
|
+
},
|
|
804
|
+
started_at: started,
|
|
805
|
+
finished_at: new Date().toISOString(),
|
|
806
|
+
error: "Refusing execute of open/untrusted package without --allow-untrusted",
|
|
807
|
+
};
|
|
808
|
+
}
|
|
603
809
|
return runSkillPackage(unpacked.raw, host, options);
|
|
604
810
|
}
|
|
605
|
-
export { inspectSkill, validatePackageBytes, unpackSkill, explainPackage as explain };
|
|
811
|
+
export { inspectSkill, inspectTrustView, validatePackageBytes, unpackSkill, explainPackage as explain, };
|
package/package.json
CHANGED
|
@@ -1,12 +1,21 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@dot-skill/runtime",
|
|
3
|
-
"version": "0.
|
|
4
|
-
"description": "
|
|
3
|
+
"version": "0.5.0",
|
|
4
|
+
"description": "Inspect, dry-run, and execute portable .skill packages",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"author": {
|
|
7
7
|
"name": "Bharat Dudeja",
|
|
8
8
|
"url": "https://github.com/bharatdudeja13-cmd"
|
|
9
9
|
},
|
|
10
|
+
"keywords": [
|
|
11
|
+
"skill",
|
|
12
|
+
"dot-skill",
|
|
13
|
+
".skill",
|
|
14
|
+
"ai-agents",
|
|
15
|
+
"agent-skills",
|
|
16
|
+
"runtime",
|
|
17
|
+
"dry-run"
|
|
18
|
+
],
|
|
10
19
|
"type": "module",
|
|
11
20
|
"main": "./dist/index.js",
|
|
12
21
|
"types": "./dist/index.d.ts",
|
|
@@ -18,8 +27,14 @@
|
|
|
18
27
|
"url": "https://github.com/dot-skill/dot-skill.git",
|
|
19
28
|
"directory": "packages/runtime"
|
|
20
29
|
},
|
|
30
|
+
"homepage": "https://github.com/dot-skill/dot-skill#readme",
|
|
31
|
+
"bugs": {
|
|
32
|
+
"url": "https://github.com/dot-skill/dot-skill/issues"
|
|
33
|
+
},
|
|
21
34
|
"files": [
|
|
22
|
-
"dist"
|
|
35
|
+
"dist",
|
|
36
|
+
"README.md",
|
|
37
|
+
"LICENSE"
|
|
23
38
|
],
|
|
24
39
|
"exports": {
|
|
25
40
|
".": {
|
|
@@ -33,8 +48,8 @@
|
|
|
33
48
|
"clean": "rm -rf dist"
|
|
34
49
|
},
|
|
35
50
|
"dependencies": {
|
|
36
|
-
"@dot-skill/core": "^0.
|
|
37
|
-
"@dot-skill/protocol": "^0.
|
|
51
|
+
"@dot-skill/core": "^0.5.0",
|
|
52
|
+
"@dot-skill/protocol": "^0.5.0"
|
|
38
53
|
},
|
|
39
54
|
"devDependencies": {
|
|
40
55
|
"@types/node": "^26.1.1",
|