@dot-skill/runtime 0.4.3 → 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 +219 -20
- 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,6 +1,6 @@
|
|
|
1
1
|
import { randomUUID } from "node:crypto";
|
|
2
2
|
import { readFileSync } from "node:fs";
|
|
3
|
-
import { inspectSkill, unpackSkill, validatePackageBytes, verifyMintTrust, sha256Digest, } from "@dot-skill/core";
|
|
3
|
+
import { inspectSkill, inspectTrustView, unpackSkill, validatePackageBytes, verifyMintTrust, sha256Digest, } from "@dot-skill/core";
|
|
4
4
|
function loadRuntimeIdentity() {
|
|
5
5
|
const metadata = JSON.parse(readFileSync(new URL("../package.json", import.meta.url), "utf8"));
|
|
6
6
|
if (typeof metadata.name !== "string" || typeof metadata.version !== "string") {
|
|
@@ -9,6 +9,82 @@ function loadRuntimeIdentity() {
|
|
|
9
9
|
return { name: metadata.name, version: metadata.version };
|
|
10
10
|
}
|
|
11
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
|
+
}
|
|
12
88
|
function substitute(template, inputs) {
|
|
13
89
|
return template.replace(/\{\{\s*([a-zA-Z_][a-zA-Z0-9_]*)\s*\}\}/g, (_, name) => {
|
|
14
90
|
const v = inputs[name];
|
|
@@ -126,16 +202,28 @@ export async function runSkillPackage(pkg, host = {}, options = {}) {
|
|
|
126
202
|
error: `Missing required inputs: ${missing.map((m) => m.name).join(", ")}`,
|
|
127
203
|
};
|
|
128
204
|
}
|
|
129
|
-
const consentNeeded = pkg.manifest.permissions
|
|
205
|
+
const consentNeeded = new Set(pkg.manifest.permissions
|
|
130
206
|
.filter((p) => p.requires_consent)
|
|
131
|
-
.map((p) => p.side_effect_class);
|
|
132
|
-
|
|
133
|
-
|
|
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({
|
|
134
219
|
title: pkg.manifest.title,
|
|
135
|
-
permissions: consentNeeded,
|
|
220
|
+
permissions: [...consentNeeded],
|
|
136
221
|
steps: pkg.workflow.steps.map((s) => `${s.id}:${s.kind}`),
|
|
137
222
|
});
|
|
138
|
-
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) {
|
|
139
227
|
return {
|
|
140
228
|
kind: "skill_run",
|
|
141
229
|
id: runId,
|
|
@@ -389,6 +477,12 @@ async function executeStep(step, ctx) {
|
|
|
389
477
|
const cap = ctx.pkg.manifest.capabilities.find((c) => c.name === step.capability);
|
|
390
478
|
if (!cap)
|
|
391
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);
|
|
392
486
|
if (ctx.dry) {
|
|
393
487
|
return {
|
|
394
488
|
output: { dry_run: true, capability: cap.name, arguments: step.arguments },
|
|
@@ -406,10 +500,6 @@ async function executeStep(step, ctx) {
|
|
|
406
500
|
}
|
|
407
501
|
throw new Error(`No adapter for capability ${cap.name}`);
|
|
408
502
|
}
|
|
409
|
-
const args = { ...(step.arguments ?? {}) };
|
|
410
|
-
for (const [k, bind] of Object.entries(step.argument_bindings ?? {})) {
|
|
411
|
-
args[k] = ctx.inputs[bind] ?? ctx.stepOutputs[bind];
|
|
412
|
-
}
|
|
413
503
|
const inv = await adapter.invoke(cap, args);
|
|
414
504
|
if (!inv.ok)
|
|
415
505
|
throw new Error(inv.error ?? "tool failed");
|
|
@@ -471,11 +561,18 @@ async function executeStep(step, ctx) {
|
|
|
471
561
|
resultAs: step.result_as,
|
|
472
562
|
};
|
|
473
563
|
}
|
|
474
|
-
|
|
475
|
-
|
|
476
|
-
return { output: existing, resultAs: step.result_as };
|
|
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`);
|
|
477
566
|
}
|
|
478
|
-
|
|
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`);
|
|
574
|
+
}
|
|
575
|
+
return { output: evidence, resultAs: step.result_as };
|
|
479
576
|
}
|
|
480
577
|
case "verify": {
|
|
481
578
|
const results = [];
|
|
@@ -505,6 +602,34 @@ async function executeStep(step, ctx) {
|
|
|
505
602
|
const val = ctx.stepOutputs[key] ?? ctx.inputs[key];
|
|
506
603
|
results.push({ assertion, passed: val !== undefined });
|
|
507
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
|
+
}
|
|
508
633
|
else {
|
|
509
634
|
results.push({
|
|
510
635
|
assertion,
|
|
@@ -536,8 +661,9 @@ async function executeStep(step, ctx) {
|
|
|
536
661
|
}
|
|
537
662
|
function evalWhen(expr, inputs, stepOutputs) {
|
|
538
663
|
const m = expr.match(/^input:([a-zA-Z0-9_]+)(?:==(.*))?$/);
|
|
539
|
-
if (!m)
|
|
540
|
-
|
|
664
|
+
if (!m) {
|
|
665
|
+
throw new Error(`Unsupported branch expression ${JSON.stringify(expr)}; supported form is input:name or input:name==value`);
|
|
666
|
+
}
|
|
541
667
|
const val = inputs[m[1]] ?? stepOutputs[m[1]];
|
|
542
668
|
if (m[2] === undefined)
|
|
543
669
|
return Boolean(val);
|
|
@@ -570,6 +696,42 @@ export async function runSkillArchive(archive, host = {}, options = {}) {
|
|
|
570
696
|
}
|
|
571
697
|
const unpacked = unpackSkill(archive);
|
|
572
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
|
+
}
|
|
573
735
|
const trustProfile = options.trust_profile ??
|
|
574
736
|
(manifest.policy.require_anchor
|
|
575
737
|
? "anchored"
|
|
@@ -577,7 +739,13 @@ export async function runSkillArchive(archive, host = {}, options = {}) {
|
|
|
577
739
|
? "minted"
|
|
578
740
|
: manifest.policy.trust_profile ?? "open");
|
|
579
741
|
if (trustProfile !== "open") {
|
|
580
|
-
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
|
+
});
|
|
581
749
|
if (!trust.ok) {
|
|
582
750
|
const started = new Date().toISOString();
|
|
583
751
|
return {
|
|
@@ -587,7 +755,7 @@ export async function runSkillArchive(archive, host = {}, options = {}) {
|
|
|
587
755
|
skill_version: manifest.version,
|
|
588
756
|
package_digest: manifest.package_digest,
|
|
589
757
|
status: "failed",
|
|
590
|
-
mode
|
|
758
|
+
mode,
|
|
591
759
|
resolved_inputs: {},
|
|
592
760
|
steps: [],
|
|
593
761
|
verifications: trust.issues.map((issue) => ({
|
|
@@ -607,6 +775,37 @@ export async function runSkillArchive(archive, host = {}, options = {}) {
|
|
|
607
775
|
};
|
|
608
776
|
}
|
|
609
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
|
+
}
|
|
610
809
|
return runSkillPackage(unpacked.raw, host, options);
|
|
611
810
|
}
|
|
612
|
-
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",
|