stitchkit 0.90.3 → 0.90.4
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 +74 -0
- package/dist/agent-runtime-coding-tools.js +3 -3
- package/dist/agent-runtime-harness.js +4 -4
- package/dist/agent-runtime-sandbox.js +3 -3
- package/dist/agent-runtime.js +7 -7
- package/dist/cli.d.ts +14 -1
- package/dist/cli.d.ts.map +1 -1
- package/dist/cli.js +359 -11
- package/dist/{index-gbqjt8jz.js → index-19ryv24q.js} +3 -3
- package/dist/{index-2hrfpw2c.js → index-32vjke6q.js} +2 -2
- package/dist/{index-f475yj00.js → index-6atvfjc1.js} +1 -1
- package/dist/{index-79hb1wyh.js → index-7t0wq6j5.js} +3 -3
- package/dist/{index-j7q0xj6d.js → index-8crm1srv.js} +1 -1
- package/dist/{index-kc6h6hg0.js → index-8z9we758.js} +1 -1
- package/dist/{index-r159gjwy.js → index-fenaekmk.js} +94 -8
- package/dist/{index-wb15909q.js → index-kzxpsf8y.js} +3 -3
- package/dist/{index-44ht2790.js → index-vcnfwrtr.js} +1 -1
- package/dist/{index-1923shw9.js → index-y47z614h.js} +214 -7
- package/dist/testing.js +1 -1
- package/dist/tool-invoker.js +4 -4
- package/dist/tools/cli-args.d.ts +22 -0
- package/dist/tools/cli-args.d.ts.map +1 -1
- package/dist/tools/cli-installer.d.ts +29 -0
- package/dist/tools/cli-installer.d.ts.map +1 -0
- package/dist/tools/cli-manifest.d.ts +83 -0
- package/dist/tools/cli-manifest.d.ts.map +1 -0
- package/dist/tools/cli-profile.d.ts +40 -0
- package/dist/tools/cli-profile.d.ts.map +1 -0
- package/dist/tools/cli-update.d.ts +64 -0
- package/dist/tools/cli-update.d.ts.map +1 -0
- package/dist/tools/cli-view.d.ts +12 -0
- package/dist/tools/cli-view.d.ts.map +1 -0
- package/dist/tools/cli.d.ts.map +1 -1
- package/dist/tools/connections/index.d.ts +2 -1
- package/dist/tools/connections/index.d.ts.map +1 -1
- package/dist/tools/connections/index.js +37 -10
- package/dist/tools/connections/mcp.d.ts +2 -1
- package/dist/tools/connections/mcp.d.ts.map +1 -1
- package/dist/tools/connections/mount.d.ts.map +1 -1
- package/dist/tools/connections/openapi.d.ts +2 -1
- package/dist/tools/connections/openapi.d.ts.map +1 -1
- package/dist/tools/connections/runtime.d.ts +17 -0
- package/dist/tools/connections/runtime.d.ts.map +1 -1
- package/dist/tools/connections/types.d.ts +18 -1
- package/dist/tools/connections/types.d.ts.map +1 -1
- package/dist/tools/json-schema-dialect.d.ts +17 -0
- package/dist/tools/json-schema-dialect.d.ts.map +1 -0
- package/dist/tools/presentation.d.ts +8 -1
- package/dist/tools/presentation.d.ts.map +1 -1
- package/dist/tools.js +8 -8
- package/llms-full.txt +203 -3
- package/package.json +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -15,6 +15,80 @@ additive**; the first breaking change landed in 0.10.0. Grep the file for
|
|
|
15
15
|
|
|
16
16
|
## [Unreleased]
|
|
17
17
|
|
|
18
|
+
## [0.90.4] — 2026-09-16
|
|
19
|
+
|
|
20
|
+
### Added
|
|
21
|
+
|
|
22
|
+
- **Aggregate views on the CLI: `--count-by`, `--sum`, `--top`, `--table`.** The
|
|
23
|
+
CLI's audience is agents and scripts, which is why output is JSON — but
|
|
24
|
+
answering "how many per status" by shipping the whole collection is a separate
|
|
25
|
+
decision, and an expensive one. Measured on a live server: a 98-record listing
|
|
26
|
+
is 34 750 characters of an agent's context window; `--count-by status` is about
|
|
27
|
+
ninety. `| jq` cannot recover that, because the bytes have already been read
|
|
28
|
+
into the conversation by the time the pipe sees them. `--by` names the grouping
|
|
29
|
+
field in every form it appears in; groups come back largest first, so `--top`
|
|
30
|
+
is a defined slice. A field no record carries is an argument error naming the
|
|
31
|
+
fields that exist, never a silent empty group, and an aggregate needs a
|
|
32
|
+
collection — over a scalar it is refused rather than guessed. Without a view
|
|
33
|
+
flag the emitted bytes are unchanged, and the flags never reach a tool
|
|
34
|
+
argument.
|
|
35
|
+
- **`createCliProfileStore` — named credential profiles that are never
|
|
36
|
+
substituted.** A CLI that talks to more than one deployment picks an
|
|
37
|
+
environment by name, and the convenient implementation of that is the unsafe
|
|
38
|
+
one: *the named profile does not exist, but exactly one is configured — use
|
|
39
|
+
it.* It is correct while a single profile exists and a wrong-environment
|
|
40
|
+
command the day a second appears. So a name given and not found is a refusal
|
|
41
|
+
that names the path and how to create it; substitution survives only where no
|
|
42
|
+
name was given and exactly one profile exists, and it is announced on stderr.
|
|
43
|
+
Files are `0600` in a `0700` directory, and a profile other users can read is
|
|
44
|
+
refused with the `chmod` that fixes it.
|
|
45
|
+
- **CLI distribution and self-update.** A build manifest schema
|
|
46
|
+
(`CliBuildManifestSchema`), an installer generated from it
|
|
47
|
+
(`renderCliInstaller`), a bounded update check (`checkCliUpdate`) and a
|
|
48
|
+
verified atomic replace (`applyCliUpdate`). The installer parses no JSON — a
|
|
49
|
+
`curl … | sh` machine has no `jq`, so the URL and digest are substituted
|
|
50
|
+
server-side. The digest covers the **decompressed** bytes, because those are
|
|
51
|
+
what will be executed. The replace is a rename inside the target's own
|
|
52
|
+
directory, so an interrupted download can never leave a half-written
|
|
53
|
+
executable on a PATH. `assertCliPublishable` refuses republishing one version
|
|
54
|
+
from a different commit, and the check has four answers — `skipped`,
|
|
55
|
+
`current`, `outdated`, `unknown` — because "could not ask" is not "up to
|
|
56
|
+
date". Checking never replaces anything by itself. → ADR 0186
|
|
57
|
+
- **`transports` on a connection — discovered tools as CLI commands.**
|
|
58
|
+
`mountConnections` and `createCli` already composed into a CLI whose surface is
|
|
59
|
+
the one the server has right now rather than the one the binary was compiled
|
|
60
|
+
against, except that discovered tools were filtered out of the CLI projection
|
|
61
|
+
with no declarative way in. Naming `transports: ['CLI']` on a connection makes
|
|
62
|
+
that server's tools commands; a connection without it still contributes
|
|
63
|
+
nothing, so exposure stays explicit.
|
|
64
|
+
- **`coerceJsonArgs`, `routeCliArgv`, `extractCliGlobalOptions` and
|
|
65
|
+
`CliArgumentError` are exported from `stitchkit/cli`.** `parseCliArgs`
|
|
66
|
+
deliberately leaves array and object values as strings for the pass that runs
|
|
67
|
+
next; a consumer that parses argv and sends the call itself needs both halves,
|
|
68
|
+
and reaching for the `stitchkit/tools` barrel to get one function pulls the MCP
|
|
69
|
+
and AI peers into a binary this entrypoint is careful to avoid.
|
|
70
|
+
|
|
71
|
+
### Fixed
|
|
72
|
+
|
|
73
|
+
- **A stitchkit MCP surface is readable by the stitchkit MCP client again.**
|
|
74
|
+
`mountConnections` threw `Reference not found:
|
|
75
|
+
#/definitions/input/definitions/__schema0` against a server that is itself
|
|
76
|
+
built on stitchkit, and on one real surface of 205 tools five carried such a
|
|
77
|
+
reference — enough to lose the whole connection, because the mount was
|
|
78
|
+
all-or-nothing. Two defects underneath. The served document declared the
|
|
79
|
+
2020-12 dialect (the SDK stamps it) while using draft-07's `definitions`
|
|
80
|
+
keyword, so any reader that registers subschemas from the keyword its dialect
|
|
81
|
+
names found nothing to resolve against; MCP metadata now moves to `$defs` and
|
|
82
|
+
carries its pointers with it. And definitions were nested one level deeper than
|
|
83
|
+
a reader looks, because namespacing wrapped each schema instead of prefixing
|
|
84
|
+
its definition names; they are hoisted to the document root now, `allOf`
|
|
85
|
+
merges included. The client also no longer believes a contradicted stamp, which
|
|
86
|
+
is how it reaches every stitchkit server published before this release. → ADR 0185
|
|
87
|
+
- **One unconvertible discovered tool no longer takes the connection down.**
|
|
88
|
+
`mountConnections` builds per tool, skips what it cannot build and names it —
|
|
89
|
+
through `onSkippedTool`, or a stderr line naming the connection, the tool and
|
|
90
|
+
the reason.
|
|
91
|
+
|
|
18
92
|
## [0.90.3] — 2026-09-15
|
|
19
93
|
|
|
20
94
|
### Added
|
|
@@ -3,15 +3,15 @@ import {
|
|
|
3
3
|
AgentCodingToolLimitsSchema,
|
|
4
4
|
AgentCodingToolPathAuthorizationSchema,
|
|
5
5
|
createAgentCodingTools
|
|
6
|
-
} from "./index-
|
|
6
|
+
} from "./index-8crm1srv.js";
|
|
7
7
|
import"./index-bn2cjajt.js";
|
|
8
8
|
import"./index-gte38nbm.js";
|
|
9
9
|
import"./index-kazec06k.js";
|
|
10
10
|
import"./index-6k1937bx.js";
|
|
11
11
|
import"./index-scs3f1eg.js";
|
|
12
12
|
import"./index-kzfs85xp.js";
|
|
13
|
-
import"./index-
|
|
14
|
-
import"./index-
|
|
13
|
+
import"./index-8z9we758.js";
|
|
14
|
+
import"./index-fenaekmk.js";
|
|
15
15
|
import"./index-cby4ar3v.js";
|
|
16
16
|
import"./index-77fekveh.js";
|
|
17
17
|
export {
|
|
@@ -6,7 +6,7 @@ import {
|
|
|
6
6
|
composeAgentPrompt,
|
|
7
7
|
createAgentRuntime,
|
|
8
8
|
createAgentRuntimeEventSink
|
|
9
|
-
} from "./index-
|
|
9
|
+
} from "./index-6atvfjc1.js";
|
|
10
10
|
import"./index-p13mwz16.js";
|
|
11
11
|
import"./index-y64k3s0h.js";
|
|
12
12
|
import {
|
|
@@ -19,7 +19,7 @@ import {
|
|
|
19
19
|
walkContainedFiles
|
|
20
20
|
} from "./index-bn2cjajt.js";
|
|
21
21
|
import"./index-7rkhw9ec.js";
|
|
22
|
-
import"./index-
|
|
22
|
+
import"./index-vcnfwrtr.js";
|
|
23
23
|
import"./index-vsbzgd7b.js";
|
|
24
24
|
import"./index-f9mb610r.js";
|
|
25
25
|
import"./index-2hryh65w.js";
|
|
@@ -31,8 +31,8 @@ import"./index-7zbps32p.js";
|
|
|
31
31
|
import"./index-kzfs85xp.js";
|
|
32
32
|
import {
|
|
33
33
|
defineRuntimeTool
|
|
34
|
-
} from "./index-
|
|
35
|
-
import"./index-
|
|
34
|
+
} from "./index-8z9we758.js";
|
|
35
|
+
import"./index-fenaekmk.js";
|
|
36
36
|
import"./index-cby4ar3v.js";
|
|
37
37
|
import"./index-77fekveh.js";
|
|
38
38
|
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import {
|
|
2
2
|
createAgentCodingTools
|
|
3
|
-
} from "./index-
|
|
3
|
+
} from "./index-8crm1srv.js";
|
|
4
4
|
import"./index-bn2cjajt.js";
|
|
5
5
|
import {
|
|
6
6
|
codingRefusal,
|
|
@@ -11,8 +11,8 @@ import"./index-kazec06k.js";
|
|
|
11
11
|
import"./index-6k1937bx.js";
|
|
12
12
|
import"./index-scs3f1eg.js";
|
|
13
13
|
import"./index-kzfs85xp.js";
|
|
14
|
-
import"./index-
|
|
15
|
-
import"./index-
|
|
14
|
+
import"./index-8z9we758.js";
|
|
15
|
+
import"./index-fenaekmk.js";
|
|
16
16
|
import"./index-cby4ar3v.js";
|
|
17
17
|
import"./index-77fekveh.js";
|
|
18
18
|
|
package/dist/agent-runtime.js
CHANGED
|
@@ -31,7 +31,7 @@ import {
|
|
|
31
31
|
renderAgentStateSlots,
|
|
32
32
|
repairedSearchCall,
|
|
33
33
|
selectAgentHistory
|
|
34
|
-
} from "./index-
|
|
34
|
+
} from "./index-6atvfjc1.js";
|
|
35
35
|
import"./index-p13mwz16.js";
|
|
36
36
|
import {
|
|
37
37
|
AgentAdmissionEventSchema,
|
|
@@ -169,11 +169,11 @@ import {
|
|
|
169
169
|
import {
|
|
170
170
|
buildToolManifest,
|
|
171
171
|
mountAgent
|
|
172
|
-
} from "./index-
|
|
172
|
+
} from "./index-19ryv24q.js";
|
|
173
173
|
import"./index-7rkhw9ec.js";
|
|
174
|
-
import"./index-
|
|
175
|
-
import"./index-
|
|
176
|
-
import"./index-
|
|
174
|
+
import"./index-kzxpsf8y.js";
|
|
175
|
+
import"./index-32vjke6q.js";
|
|
176
|
+
import"./index-vcnfwrtr.js";
|
|
177
177
|
import"./index-vsbzgd7b.js";
|
|
178
178
|
import"./index-f9mb610r.js";
|
|
179
179
|
import"./index-2hryh65w.js";
|
|
@@ -188,8 +188,8 @@ import"./index-7zbps32p.js";
|
|
|
188
188
|
import"./index-kzfs85xp.js";
|
|
189
189
|
import {
|
|
190
190
|
defineRuntimeTool
|
|
191
|
-
} from "./index-
|
|
192
|
-
import"./index-
|
|
191
|
+
} from "./index-8z9we758.js";
|
|
192
|
+
import"./index-fenaekmk.js";
|
|
193
193
|
import"./index-cby4ar3v.js";
|
|
194
194
|
import {
|
|
195
195
|
executableAgentRuntimeTools
|
package/dist/cli.d.ts
CHANGED
|
@@ -17,9 +17,22 @@
|
|
|
17
17
|
* and point your app's `package.json` `bin` at it.
|
|
18
18
|
*/
|
|
19
19
|
export { type CliConfig, type CliSurfaceSource, createCli } from './tools/cli.js';
|
|
20
|
-
export { type CliRunOptions, type ParsedCliArgs, parseCliArgs } from './tools/cli-args.js';
|
|
20
|
+
export { CliArgumentError, type CliArgvRoute, type CliGlobalOptionsParse, type CliResultView, type CliRunOptions, extractCliGlobalOptions, type ParsedCliArgs, parseCliArgs, routeCliArgv, } from './tools/cli-args.js';
|
|
21
21
|
export { type CliCommandContext, type CliCommandDefinition, type CliCommandDefinitionBase, type CliCommandDefinitionWithOutput, type CliCommandDefinitionWithoutOutput, defineCliCommand, } from './tools/cli-command.js';
|
|
22
22
|
export { type CliWriters, DEFAULT_EXIT_CODES, type EmitOptions, type ExitCodeMap, emitResult, } from './tools/cli-format.js';
|
|
23
|
+
export { type CliInstallerConfig, renderCliInstaller, } from './tools/cli-installer.js';
|
|
24
|
+
export { assertCliPublishable, type CliBuildAsset, CliBuildAssetSchema, type CliBuildManifest, CliBuildManifestSchema, type CliBuildStamp, CliBuildStampSchema, type CliBuildTarget, CliBuildTargetSchema, currentCliBuildTarget, formatCliBuildStamp, selectCliBuildAsset, } from './tools/cli-manifest.js';
|
|
23
25
|
export type { CliPresentationPolicyConfig } from './tools/cli-policy.js';
|
|
26
|
+
export { CliProfileError, type CliProfileStore, type CliProfileStoreConfig, createCliProfileStore, type ResolvedCliProfile, } from './tools/cli-profile.js';
|
|
27
|
+
export { type AppliedCliUpdate, applyCliUpdate, type CliUpdateApplyConfig, type CliUpdateCheck, type CliUpdateCheckConfig, checkCliUpdate, compareCliVersions, } from './tools/cli-update.js';
|
|
28
|
+
export { type CliViewOutput, renderCliView } from './tools/cli-view.js';
|
|
24
29
|
export { type CliWaitConfig, type PollParams, pollUntilDone } from './tools/cli-wait.js';
|
|
30
|
+
/**
|
|
31
|
+
* The second half of `parseCliArgs`. The parser leaves array and object values
|
|
32
|
+
* as strings on purpose, because `executeToolMethod` runs this pass next; a
|
|
33
|
+
* consumer that parses argv and then sends the call itself needs both halves,
|
|
34
|
+
* and reaching for the `stitchkit/tools` barrel to get one function would pull
|
|
35
|
+
* the MCP and AI peers into a binary this entrypoint keeps free of them.
|
|
36
|
+
*/
|
|
37
|
+
export { coerceJsonArgs } from './tools/coerce.js';
|
|
25
38
|
//# sourceMappingURL=cli.d.ts.map
|
package/dist/cli.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"cli.d.ts","sourceRoot":"","sources":["../src/cli.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;GAiBG;AACH,OAAO,EAAE,KAAK,SAAS,EAAE,KAAK,gBAAgB,EAAE,SAAS,EAAE,MAAM,aAAa,CAAC;AAC/E,OAAO,
|
|
1
|
+
{"version":3,"file":"cli.d.ts","sourceRoot":"","sources":["../src/cli.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;GAiBG;AACH,OAAO,EAAE,KAAK,SAAS,EAAE,KAAK,gBAAgB,EAAE,SAAS,EAAE,MAAM,aAAa,CAAC;AAC/E,OAAO,EACL,gBAAgB,EAChB,KAAK,YAAY,EACjB,KAAK,qBAAqB,EAC1B,KAAK,aAAa,EAClB,KAAK,aAAa,EAClB,uBAAuB,EACvB,KAAK,aAAa,EAClB,YAAY,EACZ,YAAY,GACb,MAAM,kBAAkB,CAAC;AAC1B,OAAO,EACL,KAAK,iBAAiB,EACtB,KAAK,oBAAoB,EACzB,KAAK,wBAAwB,EAC7B,KAAK,8BAA8B,EACnC,KAAK,iCAAiC,EACtC,gBAAgB,GACjB,MAAM,qBAAqB,CAAC;AAC7B,OAAO,EACL,KAAK,UAAU,EACf,kBAAkB,EAClB,KAAK,WAAW,EAChB,KAAK,WAAW,EAChB,UAAU,GACX,MAAM,oBAAoB,CAAC;AAC5B,OAAO,EACL,KAAK,kBAAkB,EACvB,kBAAkB,GACnB,MAAM,uBAAuB,CAAC;AAC/B,OAAO,EACL,oBAAoB,EACpB,KAAK,aAAa,EAClB,mBAAmB,EACnB,KAAK,gBAAgB,EACrB,sBAAsB,EACtB,KAAK,aAAa,EAClB,mBAAmB,EACnB,KAAK,cAAc,EACnB,oBAAoB,EACpB,qBAAqB,EACrB,mBAAmB,EACnB,mBAAmB,GACpB,MAAM,sBAAsB,CAAC;AAC9B,YAAY,EAAE,2BAA2B,EAAE,MAAM,oBAAoB,CAAC;AACtE,OAAO,EACL,eAAe,EACf,KAAK,eAAe,EACpB,KAAK,qBAAqB,EAC1B,qBAAqB,EACrB,KAAK,kBAAkB,GACxB,MAAM,qBAAqB,CAAC;AAC7B,OAAO,EACL,KAAK,gBAAgB,EACrB,cAAc,EACd,KAAK,oBAAoB,EACzB,KAAK,cAAc,EACnB,KAAK,oBAAoB,EACzB,cAAc,EACd,kBAAkB,GACnB,MAAM,oBAAoB,CAAC;AAC5B,OAAO,EAAE,KAAK,aAAa,EAAE,aAAa,EAAE,MAAM,kBAAkB,CAAC;AACrE,OAAO,EAAE,KAAK,aAAa,EAAE,KAAK,UAAU,EAAE,aAAa,EAAE,MAAM,kBAAkB,CAAC;AACtF;;;;;;GAMG;AACH,OAAO,EAAE,cAAc,EAAE,MAAM,gBAAgB,CAAC"}
|
package/dist/cli.js
CHANGED
|
@@ -1,33 +1,381 @@
|
|
|
1
1
|
import {
|
|
2
|
+
CliArgumentError,
|
|
2
3
|
DEFAULT_EXIT_CODES,
|
|
3
4
|
createCli,
|
|
4
5
|
defineCliCommand,
|
|
5
6
|
emitResult,
|
|
7
|
+
extractCliGlobalOptions,
|
|
8
|
+
fetchGuarded,
|
|
6
9
|
parseCliArgs,
|
|
7
|
-
pollUntilDone
|
|
8
|
-
|
|
10
|
+
pollUntilDone,
|
|
11
|
+
readCapped,
|
|
12
|
+
renderCliView,
|
|
13
|
+
routeCliArgv
|
|
14
|
+
} from "./index-y47z614h.js";
|
|
9
15
|
import"./index-sbdmyz75.js";
|
|
10
|
-
import"./index-
|
|
11
|
-
import"./index-
|
|
12
|
-
import"./index-
|
|
16
|
+
import"./index-kzxpsf8y.js";
|
|
17
|
+
import"./index-32vjke6q.js";
|
|
18
|
+
import"./index-vcnfwrtr.js";
|
|
13
19
|
import"./index-vsbzgd7b.js";
|
|
14
20
|
import"./index-f9mb610r.js";
|
|
15
21
|
import"./index-2hryh65w.js";
|
|
16
22
|
import"./index-kazec06k.js";
|
|
17
23
|
import"./index-6k1937bx.js";
|
|
18
|
-
import
|
|
24
|
+
import {
|
|
25
|
+
AppError
|
|
26
|
+
} from "./index-scs3f1eg.js";
|
|
19
27
|
import"./index-zcgf3gqf.js";
|
|
20
|
-
import
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
import
|
|
28
|
+
import {
|
|
29
|
+
coerceJsonArgs
|
|
30
|
+
} from "./index-7zbps32p.js";
|
|
31
|
+
import {
|
|
32
|
+
safeJsonParse
|
|
33
|
+
} from "./index-kzfs85xp.js";
|
|
34
|
+
import"./index-8z9we758.js";
|
|
35
|
+
import"./index-fenaekmk.js";
|
|
24
36
|
import"./index-cby4ar3v.js";
|
|
25
37
|
import"./index-77fekveh.js";
|
|
38
|
+
// src/tools/cli-installer.ts
|
|
39
|
+
function shellQuote(value) {
|
|
40
|
+
return `'${value.replaceAll("'", `'"'"'`)}'`;
|
|
41
|
+
}
|
|
42
|
+
function renderCliInstaller(config) {
|
|
43
|
+
const binary = config.binaryName ?? config.manifest.name;
|
|
44
|
+
const installDir = config.installDir ?? "$HOME/.local/bin";
|
|
45
|
+
const decompress = config.asset.compression === "gzip" ? ` gzip -dc "$tmp/download" > "$tmp/binary"
|
|
46
|
+
` : ` mv "$tmp/download" "$tmp/binary"
|
|
47
|
+
`;
|
|
48
|
+
return `#!/bin/sh
|
|
49
|
+
# ${binary} ${config.manifest.version} (${config.manifest.commit})
|
|
50
|
+
# Generated from the build manifest — it parses no JSON and needs no jq.
|
|
51
|
+
set -eu
|
|
52
|
+
|
|
53
|
+
url=${shellQuote(config.asset.url)}
|
|
54
|
+
sha256=${shellQuote(config.asset.sha256)}
|
|
55
|
+
size=${shellQuote(String(config.asset.size))}
|
|
56
|
+
binary=${shellQuote(binary)}
|
|
57
|
+
dir="\${INSTALL_DIR:-${installDir}}"
|
|
58
|
+
|
|
59
|
+
fetch() {
|
|
60
|
+
if command -v curl >/dev/null 2>&1; then
|
|
61
|
+
curl -fsSL "$1" -o "$2"
|
|
62
|
+
elif command -v wget >/dev/null 2>&1; then
|
|
63
|
+
wget -q "$1" -O "$2"
|
|
64
|
+
else
|
|
65
|
+
echo "need curl or wget" >&2
|
|
66
|
+
exit 1
|
|
67
|
+
fi
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
digest() {
|
|
71
|
+
if command -v sha256sum >/dev/null 2>&1; then
|
|
72
|
+
sha256sum "$1" | cut -d' ' -f1
|
|
73
|
+
elif command -v shasum >/dev/null 2>&1; then
|
|
74
|
+
shasum -a 256 "$1" | cut -d' ' -f1
|
|
75
|
+
elif command -v openssl >/dev/null 2>&1; then
|
|
76
|
+
openssl dgst -sha256 "$1" | awk '{print $NF}'
|
|
77
|
+
else
|
|
78
|
+
echo "need sha256sum, shasum or openssl" >&2
|
|
79
|
+
exit 1
|
|
80
|
+
fi
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
tmp="$(mktemp -d)"
|
|
84
|
+
trap 'rm -rf "$tmp"' EXIT
|
|
85
|
+
|
|
86
|
+
fetch "$url" "$tmp/download"
|
|
87
|
+
${decompress}
|
|
88
|
+
actual="$(digest "$tmp/binary")"
|
|
89
|
+
if [ "$actual" != "$sha256" ]; then
|
|
90
|
+
echo "checksum mismatch: expected $sha256, got $actual" >&2
|
|
91
|
+
exit 1
|
|
92
|
+
fi
|
|
93
|
+
|
|
94
|
+
actual_size="$(wc -c < "$tmp/binary" | tr -d ' ')"
|
|
95
|
+
if [ "$actual_size" != "$size" ]; then
|
|
96
|
+
echo "size mismatch: expected $size bytes, got $actual_size" >&2
|
|
97
|
+
exit 1
|
|
98
|
+
fi
|
|
99
|
+
|
|
100
|
+
mkdir -p "$dir"
|
|
101
|
+
chmod 755 "$tmp/binary"
|
|
102
|
+
# A rename, never a write over the file in place: anything else can leave a
|
|
103
|
+
# half-written executable where a working one used to be.
|
|
104
|
+
mv "$tmp/binary" "$dir/$binary"
|
|
105
|
+
|
|
106
|
+
echo "installed $binary ${config.manifest.version} to $dir/$binary"
|
|
107
|
+
case ":$PATH:" in
|
|
108
|
+
*":$dir:"*) ;;
|
|
109
|
+
*) echo "note: $dir is not on your PATH" >&2 ;;
|
|
110
|
+
esac
|
|
111
|
+
`;
|
|
112
|
+
}
|
|
113
|
+
// src/tools/cli-manifest.ts
|
|
114
|
+
import { z } from "zod";
|
|
115
|
+
var CliBuildTargetSchema = z.object({
|
|
116
|
+
platform: z.string().min(1),
|
|
117
|
+
arch: z.string().min(1)
|
|
118
|
+
});
|
|
119
|
+
var CliBuildAssetSchema = CliBuildTargetSchema.extend({
|
|
120
|
+
url: z.url(),
|
|
121
|
+
compression: z.enum(["gzip", "none"]).default("none"),
|
|
122
|
+
size: z.int().positive(),
|
|
123
|
+
sha256: z.string().regex(/^[0-9a-f]{64}$/, "sha256 must be 64 lowercase hex characters")
|
|
124
|
+
});
|
|
125
|
+
var CliBuildManifestSchema = z.object({
|
|
126
|
+
name: z.string().min(1),
|
|
127
|
+
version: z.string().min(1),
|
|
128
|
+
commit: z.string().min(1),
|
|
129
|
+
builtAt: z.iso.datetime(),
|
|
130
|
+
assets: z.array(CliBuildAssetSchema)
|
|
131
|
+
});
|
|
132
|
+
var CliBuildStampSchema = z.object({
|
|
133
|
+
version: z.string().min(1),
|
|
134
|
+
commit: z.string().min(1),
|
|
135
|
+
builtAt: z.iso.datetime()
|
|
136
|
+
});
|
|
137
|
+
function formatCliBuildStamp(stamp, name) {
|
|
138
|
+
const prefix = name ? `${name} ` : "";
|
|
139
|
+
return `${prefix}${stamp.version} (${stamp.commit.slice(0, 12)}, built ${stamp.builtAt})`;
|
|
140
|
+
}
|
|
141
|
+
function currentCliBuildTarget() {
|
|
142
|
+
return { platform: process.platform, arch: process.arch };
|
|
143
|
+
}
|
|
144
|
+
function selectCliBuildAsset(manifest, target = currentCliBuildTarget()) {
|
|
145
|
+
return manifest.assets.find((asset) => asset.platform === target.platform && asset.arch === target.arch);
|
|
146
|
+
}
|
|
147
|
+
function assertCliPublishable(previous, next) {
|
|
148
|
+
if (!previous)
|
|
149
|
+
return;
|
|
150
|
+
if (previous.version !== next.version)
|
|
151
|
+
return;
|
|
152
|
+
if (previous.commit === next.commit)
|
|
153
|
+
return;
|
|
154
|
+
throw new Error(`[stitchkit] ${next.name} ${next.version} was already published from commit ${previous.commit}; ` + `refusing to republish it from ${next.commit} — bump the version instead`);
|
|
155
|
+
}
|
|
156
|
+
// src/tools/cli-profile.ts
|
|
157
|
+
import {
|
|
158
|
+
chmodSync,
|
|
159
|
+
existsSync,
|
|
160
|
+
mkdirSync,
|
|
161
|
+
readdirSync,
|
|
162
|
+
readFileSync,
|
|
163
|
+
statSync,
|
|
164
|
+
writeFileSync
|
|
165
|
+
} from "node:fs";
|
|
166
|
+
import { join } from "node:path";
|
|
167
|
+
var PROFILE_SUFFIX = ".json";
|
|
168
|
+
var FILE_MODE = 384;
|
|
169
|
+
var DIRECTORY_MODE = 448;
|
|
170
|
+
|
|
171
|
+
class CliProfileError extends AppError {
|
|
172
|
+
constructor(message, code = "NOT_FOUND") {
|
|
173
|
+
super(code, message, code === "NOT_FOUND" ? 404 : code === "FORBIDDEN" ? 403 : 400);
|
|
174
|
+
this.name = "CliProfileError";
|
|
175
|
+
}
|
|
176
|
+
}
|
|
177
|
+
function assertProfileName(name) {
|
|
178
|
+
if (!/^[A-Za-z0-9][A-Za-z0-9._-]*$/.test(name)) {
|
|
179
|
+
throw new CliProfileError(`"${name}" is not a usable profile name — letters, digits, dot, dash and underscore`, "BAD_REQUEST");
|
|
180
|
+
}
|
|
181
|
+
}
|
|
182
|
+
function createCliProfileStore(config) {
|
|
183
|
+
const announce = config.announce ?? ((line) => process.stderr.write(`${line}
|
|
184
|
+
`));
|
|
185
|
+
const pathFor = (name) => {
|
|
186
|
+
assertProfileName(name);
|
|
187
|
+
return join(config.directory, `${name}${PROFILE_SUFFIX}`);
|
|
188
|
+
};
|
|
189
|
+
const list = () => {
|
|
190
|
+
if (!existsSync(config.directory))
|
|
191
|
+
return [];
|
|
192
|
+
return readdirSync(config.directory).filter((entry) => entry.endsWith(PROFILE_SUFFIX)).map((entry) => entry.slice(0, -PROFILE_SUFFIX.length)).sort();
|
|
193
|
+
};
|
|
194
|
+
const read = (name) => {
|
|
195
|
+
const path = pathFor(name);
|
|
196
|
+
if (!existsSync(path)) {
|
|
197
|
+
throw new CliProfileError(missingMessage(name, path, config));
|
|
198
|
+
}
|
|
199
|
+
const mode = statSync(path).mode & 63;
|
|
200
|
+
if (mode !== 0) {
|
|
201
|
+
throw new CliProfileError(`profile "${name}" is readable by other users — run: chmod 600 ${path}`, "FORBIDDEN");
|
|
202
|
+
}
|
|
203
|
+
const parsed = safeJsonParse(readFileSync(path, "utf8"));
|
|
204
|
+
const result = config.schema.safeParse(parsed);
|
|
205
|
+
if (!result.success) {
|
|
206
|
+
const issue = result.error.issues[0];
|
|
207
|
+
const field = issue?.path.join(".");
|
|
208
|
+
throw new CliProfileError(`profile "${name}" (${path}) is not usable: ${field ? `${field}: ` : ""}${issue?.message ?? "invalid"}`, "BAD_REQUEST");
|
|
209
|
+
}
|
|
210
|
+
return result.data;
|
|
211
|
+
};
|
|
212
|
+
return {
|
|
213
|
+
directory: config.directory,
|
|
214
|
+
pathFor,
|
|
215
|
+
list,
|
|
216
|
+
read,
|
|
217
|
+
write(name, value) {
|
|
218
|
+
const path = pathFor(name);
|
|
219
|
+
mkdirSync(config.directory, { recursive: true, mode: DIRECTORY_MODE });
|
|
220
|
+
writeFileSync(path, `${JSON.stringify(value, null, 2)}
|
|
221
|
+
`, { mode: FILE_MODE });
|
|
222
|
+
chmodSync(path, FILE_MODE);
|
|
223
|
+
return path;
|
|
224
|
+
},
|
|
225
|
+
resolve(name) {
|
|
226
|
+
if (name !== undefined) {
|
|
227
|
+
const path = pathFor(name);
|
|
228
|
+
return { name, path, value: read(name), substituted: false };
|
|
229
|
+
}
|
|
230
|
+
const configured = list();
|
|
231
|
+
const only = configured[0];
|
|
232
|
+
if (configured.length === 1 && only !== undefined) {
|
|
233
|
+
const path = pathFor(only);
|
|
234
|
+
announce(`[${only}] using the only configured profile (${path})`);
|
|
235
|
+
return { name: only, path, value: read(only), substituted: true };
|
|
236
|
+
}
|
|
237
|
+
if (configured.length === 0) {
|
|
238
|
+
const example = join(config.directory, `<name>${PROFILE_SUFFIX}`);
|
|
239
|
+
throw new CliProfileError(`no profile is configured in ${config.directory}${config.createHint ? ` — ${config.createHint("<name>", example)}` : ""}`);
|
|
240
|
+
}
|
|
241
|
+
throw new CliProfileError(`several profiles are configured (${configured.join(", ")}) — name one`, "BAD_REQUEST");
|
|
242
|
+
}
|
|
243
|
+
};
|
|
244
|
+
}
|
|
245
|
+
function missingMessage(name, path, config) {
|
|
246
|
+
const hint = config.createHint ? ` — ${config.createHint(name, path)}` : "";
|
|
247
|
+
return `profile "${name}" is not configured; expected ${path}${hint}`;
|
|
248
|
+
}
|
|
249
|
+
// src/tools/cli-update.ts
|
|
250
|
+
import { createHash } from "node:crypto";
|
|
251
|
+
import { chmodSync as chmodSync2, renameSync, writeFileSync as writeFileSync2 } from "node:fs";
|
|
252
|
+
import { basename, dirname, join as join2 } from "node:path";
|
|
253
|
+
import { gunzipSync } from "node:zlib";
|
|
254
|
+
var DEFAULT_CHECK_TIMEOUT_MS = 2000;
|
|
255
|
+
var DEFAULT_CHECK_INTERVAL_MS = 24 * 60 * 60 * 1000;
|
|
256
|
+
var DEFAULT_DOWNLOAD_TIMEOUT_MS = 120000;
|
|
257
|
+
var MANIFEST_MAX_BYTES = 256 * 1024;
|
|
258
|
+
var DEFAULT_MAX_ASSET_BYTES = 256 * 1024 * 1024;
|
|
259
|
+
function parseVersion(value) {
|
|
260
|
+
const match = /^v?(\d+)\.(\d+)\.(\d+)(?:-([0-9A-Za-z.-]+))?/.exec(value.trim());
|
|
261
|
+
if (!match)
|
|
262
|
+
return;
|
|
263
|
+
const numbers = [match[1], match[2], match[3]].map((part) => Number(part));
|
|
264
|
+
if (numbers.some((part) => !Number.isFinite(part)))
|
|
265
|
+
return;
|
|
266
|
+
return { numbers, prerelease: match[4] };
|
|
267
|
+
}
|
|
268
|
+
function compareCliVersions(left, right) {
|
|
269
|
+
const a = parseVersion(left);
|
|
270
|
+
const b = parseVersion(right);
|
|
271
|
+
if (!a || !b)
|
|
272
|
+
return;
|
|
273
|
+
for (let index = 0;index < 3; index++) {
|
|
274
|
+
const difference = (a.numbers[index] ?? 0) - (b.numbers[index] ?? 0);
|
|
275
|
+
if (difference !== 0)
|
|
276
|
+
return difference < 0 ? -1 : 1;
|
|
277
|
+
}
|
|
278
|
+
if (a.prerelease === b.prerelease)
|
|
279
|
+
return 0;
|
|
280
|
+
if (a.prerelease === undefined)
|
|
281
|
+
return 1;
|
|
282
|
+
if (b.prerelease === undefined)
|
|
283
|
+
return -1;
|
|
284
|
+
return a.prerelease < b.prerelease ? -1 : 1;
|
|
285
|
+
}
|
|
286
|
+
async function checkCliUpdate(config) {
|
|
287
|
+
const now = config.now ?? Date.now();
|
|
288
|
+
const interval = config.intervalMs ?? DEFAULT_CHECK_INTERVAL_MS;
|
|
289
|
+
if (config.lastCheckedAt !== undefined && now - config.lastCheckedAt < interval) {
|
|
290
|
+
return { status: "skipped", nextCheckAt: config.lastCheckedAt + interval };
|
|
291
|
+
}
|
|
292
|
+
try {
|
|
293
|
+
const response = await fetchGuarded(new URL(config.manifestUrl), config.allowPrivateHosts ?? false, {
|
|
294
|
+
timeoutMs: config.timeoutMs ?? DEFAULT_CHECK_TIMEOUT_MS,
|
|
295
|
+
bodyTimeoutMs: config.timeoutMs ?? DEFAULT_CHECK_TIMEOUT_MS
|
|
296
|
+
});
|
|
297
|
+
if (!response.ok)
|
|
298
|
+
return { status: "unknown", reason: `manifest responded ${response.status}` };
|
|
299
|
+
const body = await readCapped(response, MANIFEST_MAX_BYTES);
|
|
300
|
+
if (!body)
|
|
301
|
+
return { status: "unknown", reason: "manifest is larger than expected" };
|
|
302
|
+
const parsed = CliBuildManifestSchema.safeParse(JSON.parse(body.toString("utf8")));
|
|
303
|
+
if (!parsed.success)
|
|
304
|
+
return { status: "unknown", reason: "manifest did not validate" };
|
|
305
|
+
const manifest = parsed.data;
|
|
306
|
+
const order = compareCliVersions(config.currentVersion, manifest.version);
|
|
307
|
+
if (order === undefined) {
|
|
308
|
+
return {
|
|
309
|
+
status: "unknown",
|
|
310
|
+
reason: `cannot compare ${config.currentVersion} with ${manifest.version}`
|
|
311
|
+
};
|
|
312
|
+
}
|
|
313
|
+
if (order >= 0)
|
|
314
|
+
return { status: "current", version: manifest.version };
|
|
315
|
+
const asset = selectCliBuildAsset(manifest, config.target ?? currentCliBuildTarget());
|
|
316
|
+
return {
|
|
317
|
+
status: "outdated",
|
|
318
|
+
version: manifest.version,
|
|
319
|
+
manifest,
|
|
320
|
+
...asset && { asset }
|
|
321
|
+
};
|
|
322
|
+
} catch (error) {
|
|
323
|
+
return {
|
|
324
|
+
status: "unknown",
|
|
325
|
+
reason: error instanceof Error ? error.message : String(error)
|
|
326
|
+
};
|
|
327
|
+
}
|
|
328
|
+
}
|
|
329
|
+
async function applyCliUpdate(config) {
|
|
330
|
+
const target = config.targetPath ?? process.execPath;
|
|
331
|
+
const response = await fetchGuarded(new URL(config.asset.url), config.allowPrivateHosts ?? false, {
|
|
332
|
+
timeoutMs: config.timeoutMs ?? DEFAULT_DOWNLOAD_TIMEOUT_MS,
|
|
333
|
+
bodyTimeoutMs: config.timeoutMs ?? DEFAULT_DOWNLOAD_TIMEOUT_MS
|
|
334
|
+
});
|
|
335
|
+
if (!response.ok) {
|
|
336
|
+
throw new Error(`[stitchkit] update download responded ${response.status}`);
|
|
337
|
+
}
|
|
338
|
+
const transferred = await readCapped(response, config.maxBytes ?? DEFAULT_MAX_ASSET_BYTES);
|
|
339
|
+
if (!transferred)
|
|
340
|
+
throw new Error("[stitchkit] update download exceeded the size ceiling");
|
|
341
|
+
const bytes = config.asset.compression === "gzip" ? gunzipSync(transferred) : transferred;
|
|
342
|
+
if (bytes.length !== config.asset.size) {
|
|
343
|
+
throw new Error(`[stitchkit] update is ${bytes.length} bytes, manifest says ${config.asset.size} — refusing to install it`);
|
|
344
|
+
}
|
|
345
|
+
const sha256 = createHash("sha256").update(bytes).digest("hex");
|
|
346
|
+
if (sha256 !== config.asset.sha256) {
|
|
347
|
+
throw new Error(`[stitchkit] update digest ${sha256} does not match the manifest — refusing to install it`);
|
|
348
|
+
}
|
|
349
|
+
const staged = join2(dirname(target), `.${basename(target)}.${process.pid}.${Date.now()}`);
|
|
350
|
+
writeFileSync2(staged, bytes, { mode: 493 });
|
|
351
|
+
chmodSync2(staged, 493);
|
|
352
|
+
renameSync(staged, target);
|
|
353
|
+
return { path: target, bytes: bytes.length, sha256 };
|
|
354
|
+
}
|
|
26
355
|
export {
|
|
356
|
+
selectCliBuildAsset,
|
|
357
|
+
routeCliArgv,
|
|
358
|
+
renderCliView,
|
|
359
|
+
renderCliInstaller,
|
|
27
360
|
pollUntilDone,
|
|
28
361
|
parseCliArgs,
|
|
362
|
+
formatCliBuildStamp,
|
|
363
|
+
extractCliGlobalOptions,
|
|
29
364
|
emitResult,
|
|
30
365
|
defineCliCommand,
|
|
366
|
+
currentCliBuildTarget,
|
|
367
|
+
createCliProfileStore,
|
|
31
368
|
createCli,
|
|
32
|
-
|
|
369
|
+
compareCliVersions,
|
|
370
|
+
coerceJsonArgs,
|
|
371
|
+
checkCliUpdate,
|
|
372
|
+
assertCliPublishable,
|
|
373
|
+
applyCliUpdate,
|
|
374
|
+
DEFAULT_EXIT_CODES,
|
|
375
|
+
CliProfileError,
|
|
376
|
+
CliBuildTargetSchema,
|
|
377
|
+
CliBuildStampSchema,
|
|
378
|
+
CliBuildManifestSchema,
|
|
379
|
+
CliBuildAssetSchema,
|
|
380
|
+
CliArgumentError
|
|
33
381
|
};
|
|
@@ -4,16 +4,16 @@ import {
|
|
|
4
4
|
} from "./index-7rkhw9ec.js";
|
|
5
5
|
import {
|
|
6
6
|
collectToolSurface
|
|
7
|
-
} from "./index-
|
|
7
|
+
} from "./index-kzxpsf8y.js";
|
|
8
8
|
import {
|
|
9
9
|
createToolRunner,
|
|
10
10
|
formatToolError
|
|
11
|
-
} from "./index-
|
|
11
|
+
} from "./index-32vjke6q.js";
|
|
12
12
|
import {
|
|
13
13
|
isToolExecutionControlError,
|
|
14
14
|
toolCauseFromResult,
|
|
15
15
|
toolResultFromError
|
|
16
|
-
} from "./index-
|
|
16
|
+
} from "./index-vcnfwrtr.js";
|
|
17
17
|
import {
|
|
18
18
|
isRecord
|
|
19
19
|
} from "./index-77fekveh.js";
|
|
@@ -1,12 +1,12 @@
|
|
|
1
1
|
import {
|
|
2
2
|
executeToolMethod
|
|
3
|
-
} from "./index-
|
|
3
|
+
} from "./index-vcnfwrtr.js";
|
|
4
4
|
import {
|
|
5
5
|
assertToolExtensionCompatible,
|
|
6
6
|
mergeSchemas,
|
|
7
7
|
projectToolSurface,
|
|
8
8
|
rebuildObject
|
|
9
|
-
} from "./index-
|
|
9
|
+
} from "./index-fenaekmk.js";
|
|
10
10
|
|
|
11
11
|
// src/tools/mount.ts
|
|
12
12
|
import { z } from "zod";
|
|
@@ -1,16 +1,16 @@
|
|
|
1
1
|
import {
|
|
2
2
|
collectTools,
|
|
3
3
|
createToolRunner
|
|
4
|
-
} from "./index-
|
|
4
|
+
} from "./index-32vjke6q.js";
|
|
5
5
|
import {
|
|
6
6
|
toolErrorFromResult
|
|
7
|
-
} from "./index-
|
|
7
|
+
} from "./index-vcnfwrtr.js";
|
|
8
8
|
import {
|
|
9
9
|
AppError
|
|
10
10
|
} from "./index-scs3f1eg.js";
|
|
11
11
|
import {
|
|
12
12
|
assertUniqueToolName
|
|
13
|
-
} from "./index-
|
|
13
|
+
} from "./index-fenaekmk.js";
|
|
14
14
|
|
|
15
15
|
// src/tools/invoker.ts
|
|
16
16
|
function createToolInvoker(services, config) {
|