skillscript-runtime 0.23.0 → 0.24.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/CHANGELOG.md +17 -0
- package/dist/bootstrap-from-env.d.ts +46 -0
- package/dist/bootstrap-from-env.d.ts.map +1 -0
- package/dist/bootstrap-from-env.js +130 -0
- package/dist/bootstrap-from-env.js.map +1 -0
- package/dist/cli.js +19 -164
- package/dist/cli.js.map +1 -1
- package/dist/connectors/mcp-remote.d.ts +5 -0
- package/dist/connectors/mcp-remote.d.ts.map +1 -1
- package/dist/connectors/mcp-remote.js +51 -6
- package/dist/connectors/mcp-remote.js.map +1 -1
- package/dist/connectors/registry.d.ts +9 -0
- package/dist/connectors/registry.d.ts.map +1 -1
- package/dist/connectors/registry.js +23 -0
- package/dist/connectors/registry.js.map +1 -1
- package/dist/connectors/skill-store.d.ts +1 -0
- package/dist/connectors/skill-store.d.ts.map +1 -1
- package/dist/connectors/skill-store.js +27 -0
- package/dist/connectors/skill-store.js.map +1 -1
- package/dist/connectors/sqlite-skill-store.d.ts +1 -0
- package/dist/connectors/sqlite-skill-store.d.ts.map +1 -1
- package/dist/connectors/sqlite-skill-store.js +10 -0
- package/dist/connectors/sqlite-skill-store.js.map +1 -1
- package/dist/connectors/types.d.ts +11 -0
- package/dist/connectors/types.d.ts.map +1 -1
- package/dist/connectors/types.js.map +1 -1
- package/dist/dashboard/server.d.ts +2 -0
- package/dist/dashboard/server.d.ts.map +1 -1
- package/dist/dashboard/server.js +4 -0
- package/dist/dashboard/server.js.map +1 -1
- package/dist/dashboard/spa/app.js +33 -5
- package/dist/index.d.ts +2 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +1 -0
- package/dist/index.js.map +1 -1
- package/dist/lint.d.ts.map +1 -1
- package/dist/lint.js +10 -0
- package/dist/lint.js.map +1 -1
- package/dist/mcp-server.d.ts.map +1 -1
- package/dist/mcp-server.js +22 -1
- package/dist/mcp-server.js.map +1 -1
- package/docs/adopter-playbook.md +39 -4
- package/docs/configuration.md +2 -2
- package/docs/connector-contract-reference.md +4 -0
- package/docs/sqlite-skill-store.md +3 -2
- package/examples/connectors/SkillStoreTemplate/SkillStoreTemplate.ts +19 -0
- package/examples/custom-bootstrap.example.ts +17 -7
- package/examples/skillscripts/hello-world.skill.provenance.json +1 -1
- package/package.json +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -2,6 +2,23 @@
|
|
|
2
2
|
|
|
3
3
|
Each release carries an **Upgrade impact:** line (first in its section) so a bump's requirements are visible at a glance. Tags (closed set): **BREAKING** (a manual change is needed to keep working) · **RE-APPROVE** (secured-mode signature invalidation — skills must be re-approved before they run) · **CONFIG** (`connectors.json` / config edit needed) · **none (additive)** (no action; backward-compatible). Standard from 0.20.0 forward; the pre-0.20 transitions that need action are flagged inline below (0.14.0, 0.18.8, 0.19.0). Full walkthrough: [UPGRADING.md](UPGRADING.md).
|
|
4
4
|
|
|
5
|
+
## 0.24.0 — 2026-06-25 — adopter wishlist: bootstrapFromEnv + remote-store polling + connector reaping
|
|
6
|
+
|
|
7
|
+
**Upgrade impact:** none (additive). All four items are backward-compatible — a new export, an optional contract method, an opt-in tool param, and an internal lifecycle fix. Existing programmatic `bootstrap()` code keeps working; `bootstrapFromEnv()` is an *optional* simplification.
|
|
8
|
+
|
|
9
|
+
From an AMP-backed remote-store adopter dogfooding the runtime:
|
|
10
|
+
|
|
11
|
+
- **`bootstrapFromEnv()` — a blessed programmatic entry point.** Most adopters run `skillfile init` once, then operate via the web dashboard. This new exported helper stands that dashboard up from your own code *exactly the way the CLI does*: it loads `$SKILLSCRIPT_HOME/.env` + `skillscript.config.json` + `connectors.json`, resolves the full `SKILLSCRIPT_*` env cascade, calls `bootstrap()`, wires declarative triggers, and assembles the `DashboardServer`. Returns `{ wired, server }` (both unstarted; the caller `start()`s them and calls `wired.registry.disposeAll()` on shutdown). Precedence: explicit option > env > config.json > default. The CLI's `dashboard`/`serve` commands are now thin wrappers over it — closing the silent "every capability the CLI auto-wires had to be hand-assembled programmatically, and each omission failed silently" asymmetry.
|
|
12
|
+
- **`SkillStore.version()` — optional change-token kills the skill_list N+1 for remote stores.** Building the `skill_list` catalog loads every skill's body to parse its effectful footprint — free against a local store, but a network round-trip *per skill* against a remote one, so a polling dashboard hammered the substrate. A new optional `version()` returns a cheap store-wide token (no body loads); `skill_list` returns it as `catalog_version` and honors a caller's `if_none_match`, replying `{ not_modified: true }` and skipping the rebuild when nothing changed. The SPA now sends it (and pauses its poll when the browser tab is backgrounded). Bundled stores implement it (FS: per-file mtimes; sqlite: `(name, status, current_version)` in one query). Optional — a store without it just always rebuilds.
|
|
13
|
+
- **Connector reaping on shutdown.** `Registry.disposeAll()` (new) disposes connectors holding child processes on runtime shutdown, so a stdio-bridged MCP server child (`RemoteMcpConnector`) is reaped rather than orphaned to a dead parent across restarts. The CLI shutdown handler calls it; `bootstrapFromEnv()` callers should too. (Distinct from 0.23.1's respawn-on-death, which self-heals a child that dies *while running* — this reaps a child on *intentional* shutdown.)
|
|
14
|
+
|
|
15
|
+
## 0.23.1 — 2026-06-23 — fixes: connector-arg lint advisory + RemoteMcpConnector self-heal
|
|
16
|
+
|
|
17
|
+
**Upgrade impact:** none (additive).
|
|
18
|
+
|
|
19
|
+
- **Fix — `RemoteMcpConnector` now respawns a dead child instead of latching a permanent outage (adopter finding).** When the connector's stdio child exited (e.g. an external `SIGTERM`, exit code 143), it latched a terminal error state and **never respawned** — every subsequent dispatch returned `RemoteMcpConnector in error state` until a full runtime restart. A single child death = total connector outage. `start()` now self-heals: a dead prior session is discarded and the next dispatch (or `describeTools` warm) relaunches the child, mirroring a session reconnect. An intentional `dispose()` still blocks respawn (a post-dispose dispatch throws). Independent of what killed the child. Verified with a real spawned child that exits mid-request.
|
|
20
|
+
- **Fix — stale tier-3 advisory contradicting connector-arg lint (adopter finding).** The tier-3 `unverified-qualified-tool` advisory ("connector doesn't declare its tool surface statically; can't validate at compile time") co-fired against a connector wired via `describeTools()` but no `staticTools()` — i.e. every `RemoteMcpConnector` — and *contradicted* the 0.23.0 tier-2 connector-arg validation on the **same** op: an author saw "you passed an unknown arg (so I clearly know your schema)" alongside "I can't validate this tool." It now skips an op whose tool is verified by the warmed schema, and still fires only when neither a static nor a warmed dynamic surface is reachable (a truly-opaque connector, or a misspelled tool name not in the fetched surface). Probe-verified against the live ddg `RemoteMcpConnector`.
|
|
21
|
+
|
|
5
22
|
## 0.23.0 — 2026-06-23 — connector tool-schema discovery + connector-aware lint
|
|
6
23
|
|
|
7
24
|
**Upgrade impact:** none (additive). One thing to know: the new tier-2 lint can surface a *pre-existing* wrong-arg-name in an existing skill (it was always wrong; it just failed at dispatch before). Tier-2 is a warning — it never blocks compile or execution.
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
import { type BootstrapResult, type BootstrapOpts } from "./bootstrap.js";
|
|
2
|
+
import { DashboardServer } from "./dashboard/server.js";
|
|
3
|
+
export interface BootstrapFromEnvOptions {
|
|
4
|
+
/** "dashboard" mounts the SPA at `/`; "serve" runs headless on `/rpc`. Default "dashboard". */
|
|
5
|
+
mode?: "serve" | "dashboard";
|
|
6
|
+
/** Config root. Default `$SKILLSCRIPT_HOME` ?? `~/.skillscript`. */
|
|
7
|
+
home?: string;
|
|
8
|
+
/** Override the skillscript.config.json path (default `<home>/skillscript.config.json`). */
|
|
9
|
+
configPath?: string;
|
|
10
|
+
/** Override the connectors.json path (default `<home>/connectors.json`). */
|
|
11
|
+
connectorsConfigPath?: string;
|
|
12
|
+
/** Override the HTTP port (else env `SKILLSCRIPT_PORT` / config / 7878). */
|
|
13
|
+
port?: number;
|
|
14
|
+
/** Override the bind address (else env `SKILLSCRIPT_HOST` / config / 127.0.0.1). */
|
|
15
|
+
host?: string;
|
|
16
|
+
/**
|
|
17
|
+
* Pass-through to `bootstrap()`, merged LAST so it wins over the env/config
|
|
18
|
+
* resolution. This is how you inject CUSTOM in-process substrate instances —
|
|
19
|
+
* `overrides: { skillStore: new MyRemoteSkillStore(...) }` — while letting
|
|
20
|
+
* everything else auto-wire from `$SKILLSCRIPT_HOME`. (A custom substrate that
|
|
21
|
+
* IS expressible declaratively can instead use the `connectors.json`
|
|
22
|
+
* `{ type: "custom", module, export }` form and skip this.)
|
|
23
|
+
*/
|
|
24
|
+
overrides?: Partial<BootstrapOpts>;
|
|
25
|
+
}
|
|
26
|
+
export interface BootstrapFromEnvResult {
|
|
27
|
+
/** The wired runtime (registry, mcpServer, scheduler, skillStore, traceStore). */
|
|
28
|
+
wired: BootstrapResult;
|
|
29
|
+
/** The assembled DashboardServer — NOT started. Call `server.start()` + `wired.scheduler.start()`. */
|
|
30
|
+
server: DashboardServer;
|
|
31
|
+
}
|
|
32
|
+
/**
|
|
33
|
+
* Wire a runtime + DashboardServer from the environment, exactly as the CLI
|
|
34
|
+
* `dashboard` / `serve` commands do. Returns both UNSTARTED — the caller decides
|
|
35
|
+
* when to `scheduler.start()` + `server.start()` (and is responsible for
|
|
36
|
+
* `wired.registry.disposeAll()` on shutdown to reap connector children).
|
|
37
|
+
*
|
|
38
|
+
* Resolution precedence per knob: explicit option > `SKILLSCRIPT_*` env >
|
|
39
|
+
* `skillscript.config.json` > built-in default. Secured mode + approval keys +
|
|
40
|
+
* the shell/fs allowlists + timeouts are resolved inside `bootstrap()`; the
|
|
41
|
+
* dashboard auth token / approval passcode / caller-identity header are resolved
|
|
42
|
+
* inside `DashboardServer`. This helper layers the config-file values + home
|
|
43
|
+
* paths on top.
|
|
44
|
+
*/
|
|
45
|
+
export declare function bootstrapFromEnv(opts?: BootstrapFromEnvOptions): Promise<BootstrapFromEnvResult>;
|
|
46
|
+
//# sourceMappingURL=bootstrap-from-env.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"bootstrap-from-env.d.ts","sourceRoot":"","sources":["../src/bootstrap-from-env.ts"],"names":[],"mappings":"AAeA,OAAO,EAAsC,KAAK,eAAe,EAAE,KAAK,aAAa,EAAE,MAAM,gBAAgB,CAAC;AAC9G,OAAO,EAAE,eAAe,EAAE,MAAM,uBAAuB,CAAC;AAMxD,MAAM,WAAW,uBAAuB;IACtC,+FAA+F;IAC/F,IAAI,CAAC,EAAE,OAAO,GAAG,WAAW,CAAC;IAC7B,oEAAoE;IACpE,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,4FAA4F;IAC5F,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,4EAA4E;IAC5E,oBAAoB,CAAC,EAAE,MAAM,CAAC;IAC9B,4EAA4E;IAC5E,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,oFAAoF;IACpF,IAAI,CAAC,EAAE,MAAM,CAAC;IACd;;;;;;;OAOG;IACH,SAAS,CAAC,EAAE,OAAO,CAAC,aAAa,CAAC,CAAC;CACpC;AAED,MAAM,WAAW,sBAAsB;IACrC,kFAAkF;IAClF,KAAK,EAAE,eAAe,CAAC;IACvB,sGAAsG;IACtG,MAAM,EAAE,eAAe,CAAC;CACzB;AAED;;;;;;;;;;;;GAYG;AACH,wBAAsB,gBAAgB,CAAC,IAAI,GAAE,uBAA4B,GAAG,OAAO,CAAC,sBAAsB,CAAC,CA+D1G"}
|
|
@@ -0,0 +1,130 @@
|
|
|
1
|
+
// v0.23.x — `bootstrapFromEnv()`: one blessed entry point that wires a runtime
|
|
2
|
+
// + DashboardServer exactly the way the `skillfile dashboard` / `skillfile
|
|
3
|
+
// serve` CLI does — loading `$SKILLSCRIPT_HOME/.env`, `skillscript.config.json`,
|
|
4
|
+
// and `connectors.json`, resolving the full SKILLSCRIPT_* env cascade, then
|
|
5
|
+
// bootstrap()ing and assembling the DashboardServer.
|
|
6
|
+
//
|
|
7
|
+
// Most adopters run the CLI once for `init`, then operate via the web
|
|
8
|
+
// dashboard. This helper is the programmatic equivalent: an adopter calls it,
|
|
9
|
+
// gets a fully-wired runtime + server, and swaps only the substrates they
|
|
10
|
+
// customize — closing the silent CLI-vs-programmatic wiring asymmetry where
|
|
11
|
+
// each capability the CLI auto-wires had to be hand-assembled (and each
|
|
12
|
+
// omission failed silently). Adopter wishlist 82e17077.
|
|
13
|
+
import { homedir } from "node:os";
|
|
14
|
+
import { join } from "node:path";
|
|
15
|
+
import { bootstrap, wireDeclarativeTriggers } from "./bootstrap.js";
|
|
16
|
+
import { DashboardServer } from "./dashboard/server.js";
|
|
17
|
+
import { loadSkillscriptConfig } from "./runtime-config.js";
|
|
18
|
+
import { loadEnvFile } from "./dotenv-loader.js";
|
|
19
|
+
import { isSecuredMode, evaluateApprovalGate } from "./approval.js";
|
|
20
|
+
/**
|
|
21
|
+
* Wire a runtime + DashboardServer from the environment, exactly as the CLI
|
|
22
|
+
* `dashboard` / `serve` commands do. Returns both UNSTARTED — the caller decides
|
|
23
|
+
* when to `scheduler.start()` + `server.start()` (and is responsible for
|
|
24
|
+
* `wired.registry.disposeAll()` on shutdown to reap connector children).
|
|
25
|
+
*
|
|
26
|
+
* Resolution precedence per knob: explicit option > `SKILLSCRIPT_*` env >
|
|
27
|
+
* `skillscript.config.json` > built-in default. Secured mode + approval keys +
|
|
28
|
+
* the shell/fs allowlists + timeouts are resolved inside `bootstrap()`; the
|
|
29
|
+
* dashboard auth token / approval passcode / caller-identity header are resolved
|
|
30
|
+
* inside `DashboardServer`. This helper layers the config-file values + home
|
|
31
|
+
* paths on top.
|
|
32
|
+
*/
|
|
33
|
+
export async function bootstrapFromEnv(opts = {}) {
|
|
34
|
+
const home = opts.home ?? process.env["SKILLSCRIPT_HOME"] ?? join(homedir(), ".skillscript");
|
|
35
|
+
// Load `<home>/.env` so SKILLSCRIPT_* vars are populated — the CLI does this
|
|
36
|
+
// at startup; a programmatic adopter would otherwise miss it. Missing file is
|
|
37
|
+
// a no-op (per dotenv-loader).
|
|
38
|
+
loadEnvFile({ path: join(home, ".env") });
|
|
39
|
+
const mode = opts.mode ?? "dashboard";
|
|
40
|
+
const configPath = opts.configPath ?? join(home, "skillscript.config.json");
|
|
41
|
+
const { config: fileConfig } = loadSkillscriptConfig({ path: configPath });
|
|
42
|
+
const triggersFilePath = fileConfig.triggersFilePath ?? join(home, "triggers.json");
|
|
43
|
+
const connectorsConfigPath = opts.connectorsConfigPath ?? fileConfig.connectorsConfigPath ?? join(home, "connectors.json");
|
|
44
|
+
// env ?? config per knob, so `bootstrap()`'s own env resolution (opts win over
|
|
45
|
+
// env there) preserves the env > config > default precedence the CLI uses.
|
|
46
|
+
const enableUnsafeShell = boolEnv(process.env["SKILLSCRIPT_ENABLE_UNSAFE_SHELL"]) ?? fileConfig.enableUnsafeShell;
|
|
47
|
+
const forceAlwaysDraft = boolEnv(process.env["SKILLSCRIPT_FORCE_ALWAYS_DRAFT"]) ?? fileConfig.forceAlwaysDraft;
|
|
48
|
+
const pollIntervalSeconds = posIntEnv(process.env["SKILLSCRIPT_POLL_INTERVAL_SECONDS"]) ?? fileConfig.pollIntervalSeconds;
|
|
49
|
+
const absoluteTimeoutMs = posIntEnv(process.env["SKILLSCRIPT_ABSOLUTE_TIMEOUT_MS"]) ?? fileConfig.absoluteTimeoutMs;
|
|
50
|
+
const maxRecursionDepth = posIntEnv(process.env["SKILLSCRIPT_MAX_RECURSION_DEPTH"], 1) ?? fileConfig.maxRecursionDepth;
|
|
51
|
+
const shellAllowlist = listEnv(process.env["SKILLSCRIPT_SHELL_ALLOWLIST"]) ?? fileConfig.shellAllowlist;
|
|
52
|
+
const fsAllowlist = listEnv(process.env["SKILLSCRIPT_FS_ALLOWLIST"]) ?? fileConfig.fsAllowlist;
|
|
53
|
+
const wired = bootstrap({
|
|
54
|
+
skillsDir: fileConfig.skillsDir ?? join(home, "skills"),
|
|
55
|
+
traceDir: fileConfig.traceDir ?? join(home, "traces"),
|
|
56
|
+
dataDbPath: fileConfig.dataDbPath ?? join(home, "data.db"),
|
|
57
|
+
triggersFilePath,
|
|
58
|
+
connectorsConfigPath,
|
|
59
|
+
mode,
|
|
60
|
+
...(pollIntervalSeconds !== undefined ? { pollIntervalSeconds } : {}),
|
|
61
|
+
...(absoluteTimeoutMs !== undefined ? { absoluteTimeoutMs } : {}),
|
|
62
|
+
...(maxRecursionDepth !== undefined ? { maxRecursionDepth } : {}),
|
|
63
|
+
...(shellAllowlist !== undefined ? { shellAllowlist } : {}),
|
|
64
|
+
...(fsAllowlist !== undefined ? { fsAllowlist } : {}),
|
|
65
|
+
...(enableUnsafeShell !== undefined ? { enableUnsafeShell } : {}),
|
|
66
|
+
...(forceAlwaysDraft === true ? { forceAlwaysDraft: true } : {}),
|
|
67
|
+
// Scheduler-fired skills record traces by default.
|
|
68
|
+
trace: { mode: "on" },
|
|
69
|
+
// Adopter overrides win last — notably custom substrate INSTANCES
|
|
70
|
+
// (e.g. overrides.skillStore = new MyRemoteSkillStore(...)).
|
|
71
|
+
...opts.overrides,
|
|
72
|
+
});
|
|
73
|
+
await wireDeclarativeTriggers(wired);
|
|
74
|
+
await warnStaleApprovals(wired.skillStore);
|
|
75
|
+
const server = new DashboardServer({
|
|
76
|
+
mcpServer: wired.mcpServer,
|
|
77
|
+
mountSpa: mode === "dashboard",
|
|
78
|
+
// Scheduler is passed unconditionally: the /event route gates on its own
|
|
79
|
+
// enable flag, but the dashboard /approve route needs the scheduler to
|
|
80
|
+
// re-register a skill's declarative triggers on approval.
|
|
81
|
+
scheduler: wired.scheduler,
|
|
82
|
+
skillStore: wired.skillStore,
|
|
83
|
+
...(opts.port !== undefined ? { port: opts.port } : {}),
|
|
84
|
+
...(opts.host !== undefined ? { bindAddress: opts.host } : {}),
|
|
85
|
+
// port / host / mcpCallerIdentityHeader / eventIngress* / dashboard auth
|
|
86
|
+
// token / approval passcode / approval key file are all resolved from env by
|
|
87
|
+
// DashboardServer itself.
|
|
88
|
+
});
|
|
89
|
+
return { wired, server };
|
|
90
|
+
}
|
|
91
|
+
/** Warn (best-effort) when secured mode is on and stored Approved skills lack a valid signature. */
|
|
92
|
+
async function warnStaleApprovals(skillStore) {
|
|
93
|
+
if (!isSecuredMode())
|
|
94
|
+
return;
|
|
95
|
+
try {
|
|
96
|
+
const approved = await skillStore.query({ status: "Approved" });
|
|
97
|
+
const stale = [];
|
|
98
|
+
for (const m of approved) {
|
|
99
|
+
const loaded = await skillStore.load(m.name);
|
|
100
|
+
if (!evaluateApprovalGate(loaded.source).ok)
|
|
101
|
+
stale.push(m.name);
|
|
102
|
+
}
|
|
103
|
+
if (stale.length === 0)
|
|
104
|
+
return;
|
|
105
|
+
const shown = stale.slice(0, 5).join(", ");
|
|
106
|
+
const more = stale.length > 5 ? `, +${stale.length - 5} more` : "";
|
|
107
|
+
process.stderr.write(`\n⚠ Secured mode: ${stale.length} stored skill${stale.length === 1 ? "" : "s"} ` +
|
|
108
|
+
`${stale.length === 1 ? "is" : "are"} Approved but carry no valid signature — ` +
|
|
109
|
+
`they will be REFUSED until re-approved.\n` +
|
|
110
|
+
` Re-bless the set: skillfile reapprove --apply\n` +
|
|
111
|
+
` (${shown}${more})\n\n`);
|
|
112
|
+
}
|
|
113
|
+
catch {
|
|
114
|
+
/* best-effort — a scan failure must never block the runtime starting */
|
|
115
|
+
}
|
|
116
|
+
}
|
|
117
|
+
// ─── env coercion helpers (mirror the CLI's cascade semantics) ───────────────
|
|
118
|
+
function boolEnv(raw) {
|
|
119
|
+
return raw !== undefined ? raw === "true" : undefined;
|
|
120
|
+
}
|
|
121
|
+
function posIntEnv(raw, min = 0) {
|
|
122
|
+
if (raw === undefined)
|
|
123
|
+
return undefined;
|
|
124
|
+
const n = Number(raw);
|
|
125
|
+
return Number.isInteger(n) && n >= (min || 1) ? n : undefined;
|
|
126
|
+
}
|
|
127
|
+
function listEnv(raw) {
|
|
128
|
+
return raw !== undefined ? raw.split(",").map((s) => s.trim()).filter((s) => s.length > 0) : undefined;
|
|
129
|
+
}
|
|
130
|
+
//# sourceMappingURL=bootstrap-from-env.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"bootstrap-from-env.js","sourceRoot":"","sources":["../src/bootstrap-from-env.ts"],"names":[],"mappings":"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"}
|
package/dist/cli.js
CHANGED
|
@@ -25,9 +25,8 @@ import { SqliteDataStore } from "./connectors/data-store.js";
|
|
|
25
25
|
import { parse } from "./parser.js";
|
|
26
26
|
import { FilesystemTraceStore } from "./trace.js";
|
|
27
27
|
import { healthMetrics } from "./metrics.js";
|
|
28
|
-
import {
|
|
29
|
-
import {
|
|
30
|
-
import { loadSkillscriptConfig } from "./runtime-config.js";
|
|
28
|
+
import { defaultRegistry, ensureApprovalKeys, defaultApprovalKeyFile, defaultApprovalPublicKeyFile } from "./bootstrap.js";
|
|
29
|
+
import { bootstrapFromEnv } from "./bootstrap-from-env.js";
|
|
31
30
|
import { loadEnvFile } from "./dotenv-loader.js";
|
|
32
31
|
import { createHash } from "node:crypto";
|
|
33
32
|
const HOME_DIR = process.env["SKILLSCRIPT_HOME"] ?? join(homedir(), ".skillscript");
|
|
@@ -907,34 +906,6 @@ async function cmdReapprove(args) {
|
|
|
907
906
|
process.stdout.write(`\nDone — ${ok} re-blessed${fail > 0 ? `, ${fail} failed` : ""}.\n`);
|
|
908
907
|
return fail === 0 ? 0 : 1;
|
|
909
908
|
}
|
|
910
|
-
// v0.20.1 — secured-mode startup nudge. Scans the store for skills that are
|
|
911
|
-
// Approved but fail the gate (no/legacy/invalid signature) and warns once at
|
|
912
|
-
// boot, pointing at `skillfile reapprove`. Best-effort: never blocks startup.
|
|
913
|
-
async function warnStaleApprovals(skillStore) {
|
|
914
|
-
if (!isSecuredMode())
|
|
915
|
-
return;
|
|
916
|
-
try {
|
|
917
|
-
const approved = await skillStore.query({ status: "Approved" });
|
|
918
|
-
const stale = [];
|
|
919
|
-
for (const m of approved) {
|
|
920
|
-
const loaded = await skillStore.load(m.name);
|
|
921
|
-
if (!evaluateApprovalGate(loaded.source).ok)
|
|
922
|
-
stale.push(m.name);
|
|
923
|
-
}
|
|
924
|
-
if (stale.length === 0)
|
|
925
|
-
return;
|
|
926
|
-
const shown = stale.slice(0, 5).join(", ");
|
|
927
|
-
const more = stale.length > 5 ? `, +${stale.length - 5} more` : "";
|
|
928
|
-
process.stderr.write(`\n⚠ Secured mode: ${stale.length} stored skill${stale.length === 1 ? "" : "s"} ` +
|
|
929
|
-
`${stale.length === 1 ? "is" : "are"} Approved but carry no valid signature — ` +
|
|
930
|
-
`they will be REFUSED until re-approved.\n` +
|
|
931
|
-
` Re-bless the set: skillfile reapprove --apply\n` +
|
|
932
|
-
` (${shown}${more})\n\n`);
|
|
933
|
-
}
|
|
934
|
-
catch {
|
|
935
|
-
/* best-effort — a scan failure must never block the runtime starting */
|
|
936
|
-
}
|
|
937
|
-
}
|
|
938
909
|
function parseRunCompileArgs(args) {
|
|
939
910
|
const opts = { inputs: {}, format: "prompt", mechanical: false, inlineProvenance: false };
|
|
940
911
|
for (let i = 0; i < args.length; i++) {
|
|
@@ -1066,144 +1037,23 @@ async function copyScaffoldFile(src, dest) {
|
|
|
1066
1037
|
await writeFile(dest, body, "utf8");
|
|
1067
1038
|
}
|
|
1068
1039
|
async function cmdRuntimeHost(args, opts) {
|
|
1069
|
-
// v0.
|
|
1070
|
-
//
|
|
1071
|
-
|
|
1072
|
-
|
|
1073
|
-
|
|
1074
|
-
|
|
1075
|
-
// v0.17.4 — env-cascade for operator-config switches. Precedence:
|
|
1076
|
-
// CLI flag (most specific, per-invocation) > env var (per-process,
|
|
1077
|
-
// drop in `.env`) > JSON config (per-deployment) > built-in default.
|
|
1078
|
-
// This mirrors the standard layered-config pattern adopters expect
|
|
1079
|
-
// from any well-behaved Node service.
|
|
1080
|
-
const portStr = extractFlag(args, "--port") ?? process.env["SKILLSCRIPT_PORT"];
|
|
1081
|
-
const port = portStr !== undefined ? parseInt(portStr, 10) : fileConfig.dashboard?.port ?? 7878;
|
|
1082
|
-
// --host is the bind address inside the running process. 127.0.0.1 is
|
|
1083
|
-
// the safe default for local invocation; container deployments pass
|
|
1084
|
-
// --host 0.0.0.0 so the host-side port-forward can reach the listener
|
|
1085
|
-
// (host port mapping still enforces 127.0.0.1 externally).
|
|
1086
|
-
const host = extractFlag(args, "--host") ?? process.env["SKILLSCRIPT_HOST"] ?? fileConfig.dashboard?.host ?? "127.0.0.1";
|
|
1087
|
-
// v0.17.4 — env cascade for the v0.17.0 inbound caller-identity header.
|
|
1088
|
-
// Operator-config surface; env-natural for installer workflows.
|
|
1089
|
-
const mcpCallerIdentityHeader = process.env["SKILLSCRIPT_MCP_CALLER_IDENTITY_HEADER"] ?? fileConfig.dashboard?.mcpCallerIdentityHeader;
|
|
1090
|
-
// v0.17.4 — env cascade for the unsafe-shell posture switch.
|
|
1091
|
-
// Security-relevant; env-natural so operators can flip without
|
|
1092
|
-
// editing JSON.
|
|
1093
|
-
const envUnsafeShell = process.env["SKILLSCRIPT_ENABLE_UNSAFE_SHELL"];
|
|
1094
|
-
const enableUnsafeShell = envUnsafeShell !== undefined
|
|
1095
|
-
? envUnsafeShell === "true"
|
|
1096
|
-
: fileConfig.enableUnsafeShell;
|
|
1097
|
-
const triggersFilePath = fileConfig.triggersFilePath ?? join(HOME_DIR, "triggers.json");
|
|
1098
|
-
// v0.4.3 — auto-discover connectors.json from HOME_DIR. Closes the
|
|
1099
|
-
// last-mile gap of the v0.4.x arc: pre-v0.4.3 the loader + lint +
|
|
1100
|
-
// runtime + allowlist all worked, but the canonical CLI entry point
|
|
1101
|
-
// didn't read connectors.json. --connectors <path> overrides the
|
|
1102
|
-
// default for non-standard layouts. Loader is graceful on missing.
|
|
1103
|
-
const connectorsConfigPath = extractFlag(args, "--connectors") ?? fileConfig.connectorsConfigPath ?? join(HOME_DIR, "connectors.json");
|
|
1104
|
-
// v0.17.4 — forceAlwaysDraft cascade: env var > config file > default
|
|
1105
|
-
// false. SKILLSCRIPT_FORCE_ALWAYS_DRAFT=true forces every outside-MCP
|
|
1106
|
-
// skill_write to land Draft regardless of body declaration. Closes
|
|
1107
|
-
// the agent-self-approval path for adopters wanting a human approval
|
|
1108
|
-
// gate. Drop a `.env` with the value, restart — done.
|
|
1109
|
-
const envForceAlwaysDraft = process.env["SKILLSCRIPT_FORCE_ALWAYS_DRAFT"];
|
|
1110
|
-
const forceAlwaysDraft = envForceAlwaysDraft !== undefined
|
|
1111
|
-
? envForceAlwaysDraft === "true"
|
|
1112
|
-
: fileConfig.forceAlwaysDraft;
|
|
1113
|
-
// v0.18.7 — env cascade for three previously-hidden operator knobs.
|
|
1114
|
-
// Each follows the same cascade shape: env > config > default. Parsing
|
|
1115
|
-
// errors (non-numeric / non-positive) are silently ignored — the
|
|
1116
|
-
// config-layer schema parser is the authoritative validator for these
|
|
1117
|
-
// fields, and env values failing here fall through to config/default.
|
|
1118
|
-
const envPollSecondsRaw = process.env["SKILLSCRIPT_POLL_INTERVAL_SECONDS"];
|
|
1119
|
-
const envPollSeconds = envPollSecondsRaw !== undefined ? Number(envPollSecondsRaw) : undefined;
|
|
1120
|
-
const pollIntervalSeconds = envPollSeconds !== undefined && Number.isFinite(envPollSeconds) && envPollSeconds > 0
|
|
1121
|
-
? envPollSeconds
|
|
1122
|
-
: fileConfig.pollIntervalSeconds;
|
|
1123
|
-
const envAbsoluteTimeoutRaw = process.env["SKILLSCRIPT_ABSOLUTE_TIMEOUT_MS"];
|
|
1124
|
-
const envAbsoluteTimeout = envAbsoluteTimeoutRaw !== undefined ? Number(envAbsoluteTimeoutRaw) : undefined;
|
|
1125
|
-
const absoluteTimeoutMs = envAbsoluteTimeout !== undefined && Number.isInteger(envAbsoluteTimeout) && envAbsoluteTimeout > 0
|
|
1126
|
-
? envAbsoluteTimeout
|
|
1127
|
-
: fileConfig.absoluteTimeoutMs;
|
|
1128
|
-
const envMaxRecursionRaw = process.env["SKILLSCRIPT_MAX_RECURSION_DEPTH"];
|
|
1129
|
-
const envMaxRecursion = envMaxRecursionRaw !== undefined ? Number(envMaxRecursionRaw) : undefined;
|
|
1130
|
-
const maxRecursionDepth = envMaxRecursion !== undefined && Number.isInteger(envMaxRecursion) && envMaxRecursion >= 1
|
|
1131
|
-
? envMaxRecursion
|
|
1132
|
-
: fileConfig.maxRecursionDepth;
|
|
1133
|
-
// v0.18.8 — shell binary allowlist. Comma-separated env value, trimmed.
|
|
1134
|
-
// Default-deny: when neither env nor config is set, leave undefined →
|
|
1135
|
-
// runtime refuses ALL shell() ops. Empty env string is an explicit
|
|
1136
|
-
// empty list (also refuses all) — distinct from undefined for
|
|
1137
|
-
// observability (operator can declare "no shell at all" intentionally).
|
|
1138
|
-
const envShellAllowlistRaw = process.env["SKILLSCRIPT_SHELL_ALLOWLIST"];
|
|
1139
|
-
const shellAllowlist = envShellAllowlistRaw !== undefined
|
|
1140
|
-
? envShellAllowlistRaw.split(",").map((b) => b.trim()).filter((b) => b.length > 0)
|
|
1141
|
-
: fileConfig.shellAllowlist;
|
|
1142
|
-
// v1.0 Gate #7 — filesystem path allowlist (env > config.json), same shape as
|
|
1143
|
-
// SKILLSCRIPT_SHELL_ALLOWLIST. Default-deny when unset.
|
|
1144
|
-
const envFsAllowlistRaw = process.env["SKILLSCRIPT_FS_ALLOWLIST"];
|
|
1145
|
-
const fsAllowlist = envFsAllowlistRaw !== undefined
|
|
1146
|
-
? envFsAllowlistRaw.split(",").map((p) => p.trim()).filter((p) => p.length > 0)
|
|
1147
|
-
: fileConfig.fsAllowlist;
|
|
1148
|
-
// v0.19.0 — event ingress (memory `ceaf4579`). Two env knobs:
|
|
1149
|
-
// - SKILLSCRIPT_EVENT_INGRESS_ENABLED=true (opt-in; default off)
|
|
1150
|
-
// - SKILLSCRIPT_EVENT_INGRESS_AUTH_TOKEN=… (optional bearer-token;
|
|
1151
|
-
// when set, every POST /event requires Authorization: Bearer <token>)
|
|
1152
|
-
const envEventIngressEnabled = process.env["SKILLSCRIPT_EVENT_INGRESS_ENABLED"];
|
|
1153
|
-
const eventIngressEnabled = envEventIngressEnabled !== undefined
|
|
1154
|
-
? envEventIngressEnabled === "true"
|
|
1155
|
-
: false;
|
|
1156
|
-
const eventIngressAuthToken = process.env["SKILLSCRIPT_EVENT_INGRESS_AUTH_TOKEN"];
|
|
1157
|
-
const wired = bootstrap({
|
|
1158
|
-
skillsDir: fileConfig.skillsDir ?? SKILLS_DIR,
|
|
1159
|
-
traceDir: fileConfig.traceDir ?? TRACE_DIR,
|
|
1160
|
-
dataDbPath: fileConfig.dataDbPath ?? DATA_DB,
|
|
1161
|
-
triggersFilePath,
|
|
1162
|
-
connectorsConfigPath,
|
|
1040
|
+
// v0.23.x — the env-cascade + bootstrap + DashboardServer assembly lives in
|
|
1041
|
+
// the reusable `bootstrapFromEnv()` (so programmatic adopters wire it
|
|
1042
|
+
// identically). The CLI is a thin wrapper: CLI flags become explicit
|
|
1043
|
+
// overrides (highest precedence), then start + handle shutdown.
|
|
1044
|
+
const portFlag = extractFlag(args, "--port");
|
|
1045
|
+
const { wired, server } = await bootstrapFromEnv({
|
|
1163
1046
|
mode: opts.mode,
|
|
1164
|
-
|
|
1165
|
-
...(
|
|
1166
|
-
...(
|
|
1167
|
-
...(
|
|
1168
|
-
...(
|
|
1169
|
-
...(enableUnsafeShell !== undefined ? { enableUnsafeShell } : {}),
|
|
1170
|
-
...(forceAlwaysDraft === true ? { forceAlwaysDraft: true } : {}),
|
|
1171
|
-
// Scheduler-fired skills record traces by default; `fires` / `health` /
|
|
1172
|
-
// `health_metrics` (MCP) all read from the trace store.
|
|
1173
|
-
trace: { mode: "on" },
|
|
1047
|
+
home: HOME_DIR,
|
|
1048
|
+
...(extractFlag(args, "--config") !== undefined ? { configPath: extractFlag(args, "--config") } : {}),
|
|
1049
|
+
...(extractFlag(args, "--connectors") !== undefined ? { connectorsConfigPath: extractFlag(args, "--connectors") } : {}),
|
|
1050
|
+
...(portFlag !== undefined ? { port: parseInt(portFlag, 10) } : {}),
|
|
1051
|
+
...(extractFlag(args, "--host") !== undefined ? { host: extractFlag(args, "--host") } : {}),
|
|
1174
1052
|
});
|
|
1175
|
-
// Register declarative `# Triggers:` headers BEFORE arming the tick loop
|
|
1176
|
-
// so the first tick can fire any minute-aligned cron entries.
|
|
1177
|
-
await wireDeclarativeTriggers(wired);
|
|
1178
|
-
// v0.20.1 — when secured mode is armed, loudly flag any stored skill that's
|
|
1179
|
-
// Approved-but-unsigned (e.g. a pre-secured v1 corpus) so the operator runs
|
|
1180
|
-
// `reapprove` instead of discovering refusals skill-by-skill at runtime.
|
|
1181
|
-
await warnStaleApprovals(wired.skillStore);
|
|
1182
1053
|
wired.scheduler.start();
|
|
1183
|
-
// v0.2.7: dashboard mounts the SPA; serve runs headless on /rpc only.
|
|
1184
|
-
const server = new DashboardServer({
|
|
1185
|
-
mcpServer: wired.mcpServer,
|
|
1186
|
-
port,
|
|
1187
|
-
bindAddress: host,
|
|
1188
|
-
mountSpa: opts.mode === "dashboard",
|
|
1189
|
-
...(mcpCallerIdentityHeader !== undefined ? { mcpCallerIdentityHeader } : {}),
|
|
1190
|
-
// v0.19.0 — event ingress, off-by-default, scheduler reference required
|
|
1191
|
-
eventIngressEnabled,
|
|
1192
|
-
...(eventIngressAuthToken !== undefined ? { eventIngressAuthToken } : {}),
|
|
1193
|
-
// Scheduler is passed unconditionally: the /event route is gated on
|
|
1194
|
-
// eventIngressEnabled, but the dashboard /approve route also needs the
|
|
1195
|
-
// scheduler to re-register a skill's declarative triggers on approval
|
|
1196
|
-
// (without it, dashboard-approved cron/event skills don't fire / don't
|
|
1197
|
-
// show in the Triggers view).
|
|
1198
|
-
scheduler: wired.scheduler,
|
|
1199
|
-
// v0.20.2 — in-browser approval (passcode session-unlock). Mounts /unlock +
|
|
1200
|
-
// /approve only when SKILLSCRIPT_APPROVAL_PASSCODE is set + secured + keyed.
|
|
1201
|
-
skillStore: wired.skillStore,
|
|
1202
|
-
approvalKeyFile: process.env["SKILLSCRIPT_APPROVAL_KEY_FILE"] ?? defaultApprovalKeyFile(),
|
|
1203
|
-
});
|
|
1204
1054
|
await server.start();
|
|
1205
1055
|
const label = opts.mode === "dashboard" ? "dashboard" : "serve (headless)";
|
|
1206
|
-
process.stdout.write(`skillfile ${label} running on http://${
|
|
1056
|
+
process.stdout.write(`skillfile ${label} running on http://${server.boundAddress()}:${server.boundPort()}\nctrl-C to stop\n`);
|
|
1207
1057
|
await new Promise((resolve) => {
|
|
1208
1058
|
let shuttingDown = false;
|
|
1209
1059
|
const shutdown = () => {
|
|
@@ -1213,6 +1063,9 @@ async function cmdRuntimeHost(args, opts) {
|
|
|
1213
1063
|
void (async () => {
|
|
1214
1064
|
await wired.scheduler.stop();
|
|
1215
1065
|
await server.stop();
|
|
1066
|
+
// Reap connector child processes (stdio-bridged MCP servers) so they
|
|
1067
|
+
// don't orphan to a dead parent across restarts.
|
|
1068
|
+
await wired.registry.disposeAll();
|
|
1216
1069
|
resolve();
|
|
1217
1070
|
})();
|
|
1218
1071
|
};
|
|
@@ -1221,6 +1074,8 @@ async function cmdRuntimeHost(args, opts) {
|
|
|
1221
1074
|
});
|
|
1222
1075
|
return 0;
|
|
1223
1076
|
}
|
|
1077
|
+
// Retired: the body below is superseded by bootstrapFromEnv(); kept only as a
|
|
1078
|
+
// marker so the diff is legible. Removed in the edit that follows.
|
|
1224
1079
|
async function cmdFires(args) {
|
|
1225
1080
|
const skill = args.find((a) => !a.startsWith("--"));
|
|
1226
1081
|
if (skill === undefined) {
|