@wirecat/cli-template 0.15.0 → 0.16.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/.github/workflows/ci.yml +17 -0
- package/AGENTS.md +56 -2
- package/CHANGELOG.md +36 -0
- package/README.md +12 -4
- package/biome.json +2 -2
- package/contracts/mcp-tools.json +2 -2
- package/dist/bin/clidev.js +2 -2
- package/dist/bundle/{chunk-LU4A2T7Y.js → all-profiles-FS7AEOE7.js} +8 -5
- package/dist/bundle/{anthropic-ICTW7CNQ.js → anthropic-5RN7BGDI.js} +3 -3
- package/dist/bundle/{app-server-OW5LT5XV.js → app-server-WGTKZQKJ.js} +28 -29
- package/dist/bundle/{bun-sqlite-JE6FJFQT.js → bun-sqlite-WK2IGBGD.js} +2 -2
- package/dist/bundle/{chunk-AS2PJNQ2.js → chunk-2MWNE4L3.js} +1 -1
- package/dist/bundle/{chunk-2DAXUNTS.js → chunk-5HHIPDNW.js} +1 -1
- package/dist/bundle/{chunk-YOJLIVGO.js → chunk-5KAYQKBK.js} +1 -1
- package/dist/bundle/{chunk-62JRF2JA.js → chunk-6WOJIAYA.js} +1 -1
- package/dist/bundle/chunk-7EUX7AZV.js +141 -0
- package/dist/bundle/{chunk-7F5VMLVK.js → chunk-ADFSSIV4.js} +1 -1
- package/dist/bundle/{chunk-U6QD3J3F.js → chunk-ATHZEV2Z.js} +2 -2
- package/dist/bundle/{chunk-7UPBPE3Z.js → chunk-BPMKAZSS.js} +8 -8
- package/dist/bundle/{chunk-DZLHY4IA.js → chunk-C3GNOEJP.js} +1 -1
- package/dist/bundle/{chunk-PIJUGF5F.js → chunk-CTBHQRX6.js} +1 -1
- package/dist/bundle/{chunk-LFLSV353.js → chunk-DEHBTFIC.js} +1 -1
- package/dist/bundle/{chunk-NVNUUHT5.js → chunk-EA3TMI4A.js} +8 -8
- package/dist/bundle/{chunk-5GTMQTQH.js → chunk-EILS4BTQ.js} +16 -18
- package/dist/bundle/{chunk-EX2FV7YJ.js → chunk-ENDBOYQR.js} +14 -14
- package/dist/bundle/{chunk-6AW4DDK4.js → chunk-ETJXPB6A.js} +1 -1
- package/dist/bundle/{chunk-LATYBJ4T.js → chunk-FMO6F5Q2.js} +1 -1
- package/dist/bundle/{chunk-VVGPAHUN.js → chunk-HTYLT6ME.js} +23 -7
- package/dist/bundle/{chunk-F34HGYWF.js → chunk-JFXWLQ2L.js} +1 -1
- package/dist/bundle/{chunk-KLFYY7KC.js → chunk-JJZCPBH3.js} +6 -6
- package/dist/bundle/{chunk-G7W53MQP.js → chunk-LRHX62ZF.js} +2 -2
- package/dist/bundle/{chunk-SOYOOU2D.js → chunk-MBBOINK4.js} +2 -2
- package/dist/bundle/{chunk-NRZV7K4M.js → chunk-MEXT6XDP.js} +2 -2
- package/dist/bundle/{chunk-R2JKPHH5.js → chunk-NVPWZF67.js} +1 -1
- package/dist/bundle/{chunk-45244QQ6.js → chunk-OF56L2JU.js} +2 -2
- package/dist/bundle/{chunk-4MU2UM4C.js → chunk-OGTRPW2K.js} +3 -3
- package/dist/bundle/{chunk-VXOR76ZL.js → chunk-ORV4EGNC.js} +1 -1
- package/dist/bundle/{chunk-TJJ2XSJP.js → chunk-QGOOPPEW.js} +2 -2
- package/dist/bundle/{chunk-NH6LZ3H3.js → chunk-RFJKNFVF.js} +3 -3
- package/dist/bundle/{chunk-R5RYPD23.js → chunk-RYOAQCVJ.js} +1 -1
- package/dist/bundle/{chunk-66VMJGV6.js → chunk-SKBRW5U3.js} +1 -1
- package/dist/bundle/{chunk-NG7MOJBT.js → chunk-TXHCL75B.js} +43 -6
- package/dist/bundle/{chunk-FC53WQHG.js → chunk-VPCXYY46.js} +1 -1
- package/dist/bundle/{chunk-IOJ2DTCY.js → chunk-WDWP5N6M.js} +2 -0
- package/dist/bundle/{chunk-NVGOHYTE.js → chunk-WM5AMTRY.js} +4 -4
- package/dist/bundle/{chunk-MWUHBEDN.js → chunk-YNROUY3R.js} +10 -10
- package/dist/bundle/{chunk-DQIBGCF3.js → chunk-Z5NL6YNU.js} +2 -2
- package/dist/bundle/{chunk-GHF72SHT.js → chunk-ZSY4WAGI.js} +28 -21
- package/dist/bundle/clidev.js +3 -3
- package/dist/bundle/{command-contract-3MPKXBVL.js → command-contract-ZDU73EPH.js} +9 -9
- package/dist/bundle/{decode-NXB5UKMO.js → decode-QMLOGZ6R.js} +2 -2
- package/dist/bundle/{embed-IKZ24BMC.js → embed-PIR2LJR2.js} +4 -4
- package/dist/bundle/{embedder-GMQ24DOQ.js → embedder-6KBRPAEQ.js} +4 -4
- package/dist/bundle/{first-run-PURIGDLD.js → first-run-JO3CMY3Z.js} +7 -7
- package/dist/bundle/{first-screen-B2CF2CET.js → first-screen-3T5IXTFL.js} +10 -10
- package/dist/bundle/{node-sqlite-73Z4SFOA.js → node-sqlite-LIK6OYNF.js} +2 -2
- package/dist/bundle/{oauth-EVTPAMZP.js → oauth-7FVF7HQQ.js} +4 -4
- package/dist/bundle/{open-46WZAPJ3.js → open-CRZDEATN.js} +2 -2
- package/dist/bundle/{openai-GWRDAHDU.js → openai-WAJ6WREQ.js} +11 -32
- package/dist/bundle/{program-37NVH5YJ.js → program-ZFFFPBRS.js} +2251 -291
- package/dist/bundle/{prompt-IVEYRLS5.js → prompt-QQIVHO3M.js} +3 -3
- package/dist/bundle/{recognize-WVEKHODM.js → recognize-4KLBZDEC.js} +2 -2
- package/dist/bundle/{remote-4K5VPMC7.js → remote-BABMZTHA.js} +5 -5
- package/dist/bundle/{rerank-V24UJZYJ.js → rerank-P3IQQJIL.js} +4 -4
- package/dist/bundle/{chunk-EMHCXXNJ.js → serve-25SSAKRZ.js} +6 -4
- package/dist/bundle/server-G4GCDMYI.js +52 -0
- package/dist/bundle/{chunk-KJ3UADR3.js → server-P4XUYY5A.js} +27 -7
- package/dist/bundle/{state-ZPYGECVP.js → state-QCQALQBJ.js} +3 -3
- package/dist/bundle/{store-6JQCLPQF.js → store-ZECPRGGH.js} +7 -7
- package/dist/bundle/store-maintenance-command-ECUO5G6L.js +42 -0
- package/dist/commands/comments.d.ts +3 -0
- package/dist/commands/comments.d.ts.map +1 -0
- package/dist/commands/comments.js +114 -0
- package/dist/commands/comments.js.map +1 -0
- package/dist/commands/session.d.ts +3 -0
- package/dist/commands/session.d.ts.map +1 -0
- package/dist/commands/session.js +96 -0
- package/dist/commands/session.js.map +1 -0
- package/dist/context.d.ts +83 -0
- package/dist/context.d.ts.map +1 -0
- package/dist/context.js +58 -0
- package/dist/context.js.map +1 -0
- package/dist/program.d.ts.map +1 -1
- package/dist/program.js +24 -4
- package/dist/program.js.map +1 -1
- package/dist/sample/comments.d.ts +61 -0
- package/dist/sample/comments.d.ts.map +1 -0
- package/dist/sample/comments.js +39 -0
- package/dist/sample/comments.js.map +1 -0
- package/dist/sample/transport.d.ts +42 -0
- package/dist/sample/transport.d.ts.map +1 -0
- package/dist/sample/transport.js +152 -0
- package/dist/sample/transport.js.map +1 -0
- package/dist/token.d.ts +16 -0
- package/dist/token.d.ts.map +1 -0
- package/dist/token.js +32 -0
- package/dist/token.js.map +1 -0
- package/dist/version.d.ts +1 -1
- package/dist/version.js +1 -1
- package/docs/commands.md +234 -12
- package/docs/configuration.md +1 -1
- package/docs/dev/reference-clis.md +3 -2
- package/docs/dev/test-matrix.md +36 -5
- package/docs/index.md +4 -3
- package/docs/security.md +4 -2
- package/kit/dist/new.js +6 -3
- package/lint/overrides.json +9 -1
- package/package.json +3 -4
- package/scripts/test-matrix-untested.ts +19 -0
- package/skills/clidev-cli/SKILL.md +1 -1
- package/src/commands/comments.ts +167 -0
- package/src/commands/session.ts +144 -0
- package/src/comments.test.ts +258 -0
- package/src/context.ts +78 -0
- package/src/mcp.test.ts +71 -76
- package/src/program.test.ts +9 -16
- package/src/program.ts +28 -4
- package/src/sample/comments.ts +51 -0
- package/src/sample/transport.test.ts +117 -0
- package/src/sample/transport.ts +184 -0
- package/src/testing/fake-comments.ts +167 -0
- package/src/testing/harness.ts +51 -0
- package/src/token.ts +42 -0
- package/src/version.ts +1 -1
- package/dist/bundle/all-profiles-NVB2HT2S.js +0 -24
- package/dist/bundle/chunk-GUFPQZUA.js +0 -48
- package/dist/bundle/chunk-NL2DQPT7.js +0 -2
- package/dist/bundle/mcp-ASNYBQ5B.js +0 -152
- package/dist/bundle/mcp-EY2NUYDQ.js +0 -82
- package/dist/bundle/serve-PJWSOV7F.js +0 -19
- package/dist/bundle/server-3TPCEZ6P.js +0 -40
- package/dist/bundle/server-GQX5MZTT.js +0 -53
- package/dist/bundle/store-maintenance-command-ND4ADMGC.js +0 -42
- package/dist/commands/doctor.d.ts +0 -77
- package/dist/commands/doctor.d.ts.map +0 -1
- package/dist/commands/doctor.js +0 -74
- package/dist/commands/doctor.js.map +0 -1
- package/dist/commands/mcp.d.ts +0 -3
- package/dist/commands/mcp.d.ts.map +0 -1
- package/dist/commands/mcp.js +0 -97
- package/dist/commands/mcp.js.map +0 -1
- package/dist/mcp.d.ts +0 -29
- package/dist/mcp.d.ts.map +0 -1
- package/dist/mcp.js +0 -98
- package/dist/mcp.js.map +0 -1
- package/src/commands/doctor.ts +0 -86
- package/src/commands/mcp.ts +0 -130
- package/src/mcp-http.test.ts +0 -172
- package/src/mcp.ts +0 -140
package/dist/context.js
ADDED
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
import { CliError } from "@wirecat/cli-base";
|
|
2
|
+
import { baseContext, environmentOf, } from "@wirecat/cli-core/cli";
|
|
3
|
+
import { Pacer, pacePathFor } from "@wirecat/cli-core/sends";
|
|
4
|
+
import { configuration } from "./config.js";
|
|
5
|
+
import { IDENTITY } from "./identity.js";
|
|
6
|
+
import { commentsService } from "./sample/comments.js";
|
|
7
|
+
import { SERVICE, transport } from "./sample/transport.js";
|
|
8
|
+
import { tokenStore } from "./token.js";
|
|
9
|
+
// SAMPLE: your service's documented limit; this one is made up.
|
|
10
|
+
const PACE = { perMinute: 120, burst: 20 };
|
|
11
|
+
/**
|
|
12
|
+
* Settings, keyring and the run come from cli-core's `baseContext` and nowhere else: a command that resolves settings
|
|
13
|
+
* on its own skips `--timeout`, the run record and the profile's permissions (WC-LIFE-5, WC-LIFE-15).
|
|
14
|
+
*/
|
|
15
|
+
export const contextOf = (command) => {
|
|
16
|
+
const base = baseContext(command, configuration.resolveSettings);
|
|
17
|
+
return {
|
|
18
|
+
...base,
|
|
19
|
+
environment: environmentOf(command),
|
|
20
|
+
flags: command.optsWithGlobals(),
|
|
21
|
+
profile: base.settings.profile,
|
|
22
|
+
tokens: tokenStore(base.env, base.keyring, base.renderer.warn),
|
|
23
|
+
};
|
|
24
|
+
};
|
|
25
|
+
export const signInWords = (profile) => `\`${IDENTITY.command} ${profile} session start\``;
|
|
26
|
+
export const refuseOffline = (flags) => {
|
|
27
|
+
if (flags.offline)
|
|
28
|
+
throw new CliError("permission_error", `this needs ${SERVICE}; leave out --offline`, { reason: "offline.refused" });
|
|
29
|
+
};
|
|
30
|
+
/** The service for one run, with the token it was given; `events` comes from `run()`, so each request is recorded. */
|
|
31
|
+
export const serviceFor = (command, token, events) => {
|
|
32
|
+
const { environment, env, profile } = contextOf(command);
|
|
33
|
+
return commentsService(transport({
|
|
34
|
+
token,
|
|
35
|
+
events,
|
|
36
|
+
signal: environment.signal,
|
|
37
|
+
...(environment.fetch ? { fetch: environment.fetch } : {}),
|
|
38
|
+
pacer: new Pacer(pacePathFor(IDENTITY, profile, env), PACE),
|
|
39
|
+
}));
|
|
40
|
+
};
|
|
41
|
+
/** The profile's saved token, or an authentication error that says how to sign in. Read before any request. */
|
|
42
|
+
export const tokenOf = (command) => {
|
|
43
|
+
const { flags, profile, tokens } = contextOf(command);
|
|
44
|
+
refuseOffline(flags);
|
|
45
|
+
const saved = tokens.read(profile);
|
|
46
|
+
if (!saved)
|
|
47
|
+
throw new CliError("authentication_error", `profile ${profile} is not signed in — run ${signInWords(profile)}`);
|
|
48
|
+
return saved.secret;
|
|
49
|
+
};
|
|
50
|
+
/**
|
|
51
|
+
* Every remote call goes through here: inside cli-core's run, so `--timeout` bounds it, a failure or `--record` keeps
|
|
52
|
+
* a run named after the command, and the transport's requests land in that run (WC-LIFE-5, WC-LIFE-15).
|
|
53
|
+
*/
|
|
54
|
+
export const withService = (command, body) => {
|
|
55
|
+
const token = tokenOf(command);
|
|
56
|
+
return contextOf(command).run((events) => body(serviceFor(command, token, events)));
|
|
57
|
+
};
|
|
58
|
+
//# sourceMappingURL=context.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"context.js","sourceRoot":"","sources":["../src/context.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,QAAQ,EAAE,MAAM,mBAAmB,CAAA;AAC5C,OAAO,EAEL,WAAW,EAEX,aAAa,GAEd,MAAM,uBAAuB,CAAA;AAC9B,OAAO,EAAE,KAAK,EAAE,WAAW,EAAE,MAAM,yBAAyB,CAAA;AAE5D,OAAO,EAAE,aAAa,EAAE,MAAM,aAAa,CAAA;AAC3C,OAAO,EAAE,QAAQ,EAAE,MAAM,eAAe,CAAA;AACxC,OAAO,EAAwB,eAAe,EAAE,MAAM,sBAAsB,CAAA;AAC5E,OAAO,EAAE,OAAO,EAAE,SAAS,EAAE,MAAM,uBAAuB,CAAA;AAC1D,OAAO,EAAE,UAAU,EAAE,MAAM,YAAY,CAAA;AAOvC,gEAAgE;AAChE,MAAM,IAAI,GAAG,EAAE,SAAS,EAAE,GAAG,EAAE,KAAK,EAAE,EAAE,EAAE,CAAA;AAE1C;;;GAGG;AACH,MAAM,CAAC,MAAM,SAAS,GAAG,CAAC,OAAgB,EAAE,EAAE;IAC5C,MAAM,IAAI,GAAG,WAAW,CAAC,OAAO,EAAE,aAAa,CAAC,eAAe,CAAC,CAAA;IAChE,OAAO;QACL,GAAG,IAAI;QACP,WAAW,EAAE,aAAa,CAAqB,OAAO,CAAC;QACvD,KAAK,EAAE,OAAO,CAAC,eAAe,EAAe;QAC7C,OAAO,EAAE,IAAI,CAAC,QAAQ,CAAC,OAAO;QAC9B,MAAM,EAAE,UAAU,CAAC,IAAI,CAAC,GAAG,EAAE,IAAI,CAAC,OAAO,EAAE,IAAI,CAAC,QAAQ,CAAC,IAAI,CAAC;KAC/D,CAAA;AACH,CAAC,CAAA;AAED,MAAM,CAAC,MAAM,WAAW,GAAG,CAAC,OAAe,EAAE,EAAE,CAAC,KAAK,QAAQ,CAAC,OAAO,IAAI,OAAO,kBAAkB,CAAA;AAElG,MAAM,CAAC,MAAM,aAAa,GAAG,CAAC,KAAkB,EAAE,EAAE;IAClD,IAAI,KAAK,CAAC,OAAO;QACf,MAAM,IAAI,QAAQ,CAAC,kBAAkB,EAAE,cAAc,OAAO,uBAAuB,EAAE,EAAE,MAAM,EAAE,iBAAiB,EAAE,CAAC,CAAA;AACvH,CAAC,CAAA;AAED,sHAAsH;AACtH,MAAM,CAAC,MAAM,UAAU,GAAG,CAAC,OAAgB,EAAE,KAAa,EAAE,MAAiB,EAAE,EAAE;IAC/E,MAAM,EAAE,WAAW,EAAE,GAAG,EAAE,OAAO,EAAE,GAAG,SAAS,CAAC,OAAO,CAAC,CAAA;IACxD,OAAO,eAAe,CACpB,SAAS,CAAC;QACR,KAAK;QACL,MAAM;QACN,MAAM,EAAE,WAAW,CAAC,MAAM;QAC1B,GAAG,CAAC,WAAW,CAAC,KAAK,CAAC,CAAC,CAAC,EAAE,KAAK,EAAE,WAAW,CAAC,KAAK,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;QAC1D,KAAK,EAAE,IAAI,KAAK,CAAC,WAAW,CAAC,QAAQ,EAAE,OAAO,EAAE,GAAG,CAAC,EAAE,IAAI,CAAC;KAC5D,CAAC,CACH,CAAA;AACH,CAAC,CAAA;AAED,+GAA+G;AAC/G,MAAM,CAAC,MAAM,OAAO,GAAG,CAAC,OAAgB,EAAU,EAAE;IAClD,MAAM,EAAE,KAAK,EAAE,OAAO,EAAE,MAAM,EAAE,GAAG,SAAS,CAAC,OAAO,CAAC,CAAA;IACrD,aAAa,CAAC,KAAK,CAAC,CAAA;IACpB,MAAM,KAAK,GAAG,MAAM,CAAC,IAAI,CAAC,OAAO,CAAC,CAAA;IAClC,IAAI,CAAC,KAAK;QACR,MAAM,IAAI,QAAQ,CAAC,sBAAsB,EAAE,WAAW,OAAO,2BAA2B,WAAW,CAAC,OAAO,CAAC,EAAE,CAAC,CAAA;IACjH,OAAO,KAAK,CAAC,MAAM,CAAA;AACrB,CAAC,CAAA;AAED;;;GAGG;AACH,MAAM,CAAC,MAAM,WAAW,GAAG,CAAI,OAAgB,EAAE,IAA8C,EAAc,EAAE;IAC7G,MAAM,KAAK,GAAG,OAAO,CAAC,OAAO,CAAC,CAAA;IAC9B,OAAO,SAAS,CAAC,OAAO,CAAC,CAAC,GAAG,CAAC,CAAC,MAAM,EAAE,EAAE,CAAC,IAAI,CAAC,UAAU,CAAC,OAAO,EAAE,KAAK,EAAE,MAAM,CAAC,CAAC,CAAC,CAAA;AACrF,CAAC,CAAA"}
|
package/dist/program.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"program.d.ts","sourceRoot":"","sources":["../src/program.ts"],"names":[],"mappings":"AAEA,OAAO,
|
|
1
|
+
{"version":3,"file":"program.d.ts","sourceRoot":"","sources":["../src/program.ts"],"names":[],"mappings":"AAEA,OAAO,EAQL,KAAK,iBAAiB,EACtB,KAAK,cAAc,EACnB,KAAK,UAAU,EAKhB,MAAM,uBAAuB,CAAA;AAE9B,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,WAAW,CAAA;AAoCxC,eAAO,MAAM,UAAU,QAAO,iBAoB5B,CAAA;AAEF,eAAO,MAAM,aAAa,aAAc,cAAc,KAAG,OAAqD,CAAA;AAE9G,eAAO,MAAM,GAAG,SAAU,MAAM,EAAE,YAAY,UAAU,KAAG,OAAO,CAAC,MAAM,CAA2C,CAAA"}
|
package/dist/program.js
CHANGED
|
@@ -1,11 +1,13 @@
|
|
|
1
1
|
import { join } from "node:path";
|
|
2
2
|
import { resolvePaths } from "@wirecat/cli-base";
|
|
3
|
-
import { commandsCommand, configCommand, createProgram as createSharedProgram, outputFor, run as runShared, runsCommand, skillCommand, upgradeCommand, } from "@wirecat/cli-core/cli";
|
|
3
|
+
import { appDoctorCommand, appMcpCommand, commandsCommand, configCommand, createProgram as createSharedProgram, environmentOf, outputFor, run as runShared, runsCommand, skillCommand, upgradeCommand, } from "@wirecat/cli-core/cli";
|
|
4
4
|
import { appUpdate } from "@wirecat/cli-core/update";
|
|
5
|
-
import {
|
|
6
|
-
import {
|
|
5
|
+
import { commentsCommand } from "./commands/comments.js";
|
|
6
|
+
import { sessionCommand } from "./commands/session.js";
|
|
7
7
|
import { configuration } from "./config.js";
|
|
8
8
|
import { IDENTITY, PACKAGE, SKILL } from "./identity.js";
|
|
9
|
+
import { SERVICE } from "./sample/transport.js";
|
|
10
|
+
import { holdsToken } from "./token.js";
|
|
9
11
|
const update = appUpdate({
|
|
10
12
|
packageName: PACKAGE,
|
|
11
13
|
command: IDENTITY.command,
|
|
@@ -15,13 +17,31 @@ const update = appUpdate({
|
|
|
15
17
|
enabled: (env) => configuration.resolveSettings({}, { env, needsProfile: false }).updateCheck,
|
|
16
18
|
offVariables: [`${IDENTITY.envPrefix}_NO_UPDATE_CHECK`],
|
|
17
19
|
});
|
|
20
|
+
const mcpCommand = () => {
|
|
21
|
+
// cli-core asks `signedIn` with no keyring; the run's own one, a test's `memoryKeyring()`, is taken here. Each
|
|
22
|
+
// program builds its own `mcp`, so a tool call run beside it never swaps the server's keyring (WC-ARCH-22).
|
|
23
|
+
let keyring;
|
|
24
|
+
const mcp = appMcpCommand(IDENTITY, configuration, {
|
|
25
|
+
name: SERVICE,
|
|
26
|
+
// SAMPLE: name the kinds of text other people wrote that your answers carry (WC-MCP-11).
|
|
27
|
+
untrusted: ["comment text", "author names"],
|
|
28
|
+
permissionExample: "comments.create=deny",
|
|
29
|
+
skill: SKILL,
|
|
30
|
+
signedIn: (profile, env) => holdsToken(env, profile, keyring),
|
|
31
|
+
});
|
|
32
|
+
return mcp.hook("preAction", (_mcp, action) => {
|
|
33
|
+
keyring = environmentOf(action).keyring;
|
|
34
|
+
});
|
|
35
|
+
};
|
|
18
36
|
export const definition = () => ({
|
|
19
37
|
app: IDENTITY,
|
|
20
38
|
// The whole object: cli-core refuses a command by its key only when it gets the permission table too.
|
|
21
39
|
configuration,
|
|
22
40
|
commands: () => [
|
|
23
41
|
commandsCommand(IDENTITY),
|
|
24
|
-
|
|
42
|
+
sessionCommand(),
|
|
43
|
+
commentsCommand(),
|
|
44
|
+
appDoctorCommand(IDENTITY, configuration, { packageName: PACKAGE }),
|
|
25
45
|
skillCommand(IDENTITY, SKILL),
|
|
26
46
|
mcpCommand(),
|
|
27
47
|
upgradeCommand(IDENTITY, PACKAGE, (command) => ({
|
package/dist/program.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"program.js","sourceRoot":"","sources":["../src/program.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,IAAI,EAAE,MAAM,WAAW,CAAA;AAChC,OAAO,
|
|
1
|
+
{"version":3,"file":"program.js","sourceRoot":"","sources":["../src/program.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
import * as v from "valibot";
|
|
2
|
+
import type { Transport } from "./transport.js";
|
|
3
|
+
declare const wireComment: v.ObjectSchema<{
|
|
4
|
+
readonly id: v.StringSchema<undefined>;
|
|
5
|
+
readonly body: v.StringSchema<undefined>;
|
|
6
|
+
readonly authorName: v.StringSchema<undefined>;
|
|
7
|
+
readonly createdAt: v.StringSchema<undefined>;
|
|
8
|
+
}, undefined>;
|
|
9
|
+
/**
|
|
10
|
+
* The answer's own shape, in snake_case (WC-OUT-12). `answerEntity` publishes it, so `--json=id,text` and the MCP
|
|
11
|
+
* schema both come from here (WC-CMD-19). No wire type leaves this file (WC-ARCH-3).
|
|
12
|
+
*/
|
|
13
|
+
export declare const commentSchema: v.StrictObjectSchema<{
|
|
14
|
+
readonly id: v.StringSchema<undefined>;
|
|
15
|
+
readonly text: v.StringSchema<undefined>;
|
|
16
|
+
readonly author: v.StringSchema<undefined>;
|
|
17
|
+
readonly created_at: v.StringSchema<undefined>;
|
|
18
|
+
}, undefined>;
|
|
19
|
+
export type Comment = v.InferOutput<typeof commentSchema>;
|
|
20
|
+
export declare const commentOf: (wire: v.InferOutput<typeof wireComment>) => Comment;
|
|
21
|
+
/** Every call the sample makes: a method, a path template and only the fields the service documents. */
|
|
22
|
+
export declare const commentsService: (api: Transport) => {
|
|
23
|
+
me: () => Promise<{
|
|
24
|
+
id: string;
|
|
25
|
+
name: string;
|
|
26
|
+
}>;
|
|
27
|
+
list: ({ limit, offset, author }: {
|
|
28
|
+
limit: number;
|
|
29
|
+
offset: number;
|
|
30
|
+
author?: string | undefined;
|
|
31
|
+
}) => Promise<{
|
|
32
|
+
items: {
|
|
33
|
+
id: string;
|
|
34
|
+
text: string;
|
|
35
|
+
author: string;
|
|
36
|
+
created_at: string;
|
|
37
|
+
}[];
|
|
38
|
+
has_more: boolean;
|
|
39
|
+
}>;
|
|
40
|
+
show: (id: string) => Promise<{
|
|
41
|
+
id: string;
|
|
42
|
+
text: string;
|
|
43
|
+
author: string;
|
|
44
|
+
created_at: string;
|
|
45
|
+
}>;
|
|
46
|
+
create: (text: string) => Promise<{
|
|
47
|
+
id: string;
|
|
48
|
+
text: string;
|
|
49
|
+
author: string;
|
|
50
|
+
created_at: string;
|
|
51
|
+
}>;
|
|
52
|
+
update: (id: string, text: string) => Promise<{
|
|
53
|
+
id: string;
|
|
54
|
+
text: string;
|
|
55
|
+
author: string;
|
|
56
|
+
created_at: string;
|
|
57
|
+
}>;
|
|
58
|
+
};
|
|
59
|
+
export type CommentsService = ReturnType<typeof commentsService>;
|
|
60
|
+
export {};
|
|
61
|
+
//# sourceMappingURL=comments.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"comments.d.ts","sourceRoot":"","sources":["../../src/sample/comments.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,CAAC,MAAM,SAAS,CAAA;AAC5B,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,gBAAgB,CAAA;AAI/C,QAAA,MAAM,WAAW;;;;;aAKf,CAAA;AAIF;;;GAGG;AACH,eAAO,MAAM,aAAa;;;;;aAKxB,CAAA;AACF,MAAM,MAAM,OAAO,GAAG,CAAC,CAAC,WAAW,CAAC,OAAO,aAAa,CAAC,CAAA;AAEzD,eAAO,MAAM,SAAS,SAAU,CAAC,CAAC,WAAW,CAAC,OAAO,WAAW,CAAC,KAAG,OAKlE,CAAA;AAEF,wGAAwG;AACxG,eAAO,MAAM,eAAe,QAAS,SAAS;IAC5C,EAAE;;;;IACF,IAAI,8BAAoC;QAAE,KAAK,EAAE,MAAM,CAAC;QAAC,MAAM,EAAE,MAAM,CAAC;QAAC,MAAM,CAAC,EAAE,MAAM,GAAG,SAAS,CAAA;KAAE;;;;;;;;;IAItG,IAAI,OAAa,MAAM;;;;;;IAEvB,MAAM,SAAe,MAAM;;;;;;IAE3B,MAAM,OAAa,MAAM,QAAQ,MAAM;;;;;;CAIvC,CAAA;AAEF,MAAM,MAAM,eAAe,GAAG,UAAU,CAAC,OAAO,eAAe,CAAC,CAAA"}
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
import * as v from "valibot";
|
|
2
|
+
// SAMPLE: replace these shapes with your service's. Each wire name comes from the service's docs or a captured answer,
|
|
3
|
+
// never from memory; the strict fake in src/testing/fake-comments.ts refuses any other.
|
|
4
|
+
const wireComment = v.object({
|
|
5
|
+
id: v.string(),
|
|
6
|
+
body: v.string(),
|
|
7
|
+
authorName: v.string(),
|
|
8
|
+
createdAt: v.string(),
|
|
9
|
+
});
|
|
10
|
+
const wirePage = v.object({ comments: v.array(wireComment), hasMore: v.boolean() });
|
|
11
|
+
const wireUser = v.object({ id: v.string(), name: v.string() });
|
|
12
|
+
/**
|
|
13
|
+
* The answer's own shape, in snake_case (WC-OUT-12). `answerEntity` publishes it, so `--json=id,text` and the MCP
|
|
14
|
+
* schema both come from here (WC-CMD-19). No wire type leaves this file (WC-ARCH-3).
|
|
15
|
+
*/
|
|
16
|
+
export const commentSchema = v.strictObject({
|
|
17
|
+
id: v.string(),
|
|
18
|
+
text: v.string(),
|
|
19
|
+
author: v.string(),
|
|
20
|
+
created_at: v.string(),
|
|
21
|
+
});
|
|
22
|
+
export const commentOf = (wire) => ({
|
|
23
|
+
id: wire.id,
|
|
24
|
+
text: wire.body,
|
|
25
|
+
author: wire.authorName,
|
|
26
|
+
created_at: wire.createdAt,
|
|
27
|
+
});
|
|
28
|
+
/** Every call the sample makes: a method, a path template and only the fields the service documents. */
|
|
29
|
+
export const commentsService = (api) => ({
|
|
30
|
+
me: () => api.send({ method: "GET", path: "me" }, wireUser),
|
|
31
|
+
list: async ({ limit, offset, author }) => {
|
|
32
|
+
const page = await api.send({ method: "GET", path: "comments", query: { limit, offset, by: author } }, wirePage);
|
|
33
|
+
return { items: page.comments.map(commentOf), has_more: page.hasMore };
|
|
34
|
+
},
|
|
35
|
+
show: async (id) => commentOf(await api.send({ method: "GET", path: "comments/{id}", ids: { id } }, wireComment)),
|
|
36
|
+
create: async (text) => commentOf(await api.send({ method: "POST", path: "comments", body: { body: text } }, wireComment)),
|
|
37
|
+
update: async (id, text) => commentOf(await api.send({ method: "PATCH", path: "comments/{id}", ids: { id }, body: { body: text } }, wireComment)),
|
|
38
|
+
});
|
|
39
|
+
//# sourceMappingURL=comments.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"comments.js","sourceRoot":"","sources":["../../src/sample/comments.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
import { CliError, type SleepLike } from "@wirecat/cli-base";
|
|
2
|
+
import { type EventSink } from "@wirecat/cli-core/cli";
|
|
3
|
+
import type { Pacer } from "@wirecat/cli-core/sends";
|
|
4
|
+
import * as v from "valibot";
|
|
5
|
+
export declare const SERVICE_URL = "https://comments.example.invalid/v1/";
|
|
6
|
+
export declare const SERVICE = "Example Comments";
|
|
7
|
+
export interface Request {
|
|
8
|
+
method: "GET" | "POST" | "PATCH";
|
|
9
|
+
/** The path as a template, `comments/{id}`: the run record keeps the template, never an id. */
|
|
10
|
+
path: string;
|
|
11
|
+
ids?: Record<string, string>;
|
|
12
|
+
query?: Record<string, string | number | undefined>;
|
|
13
|
+
body?: Record<string, unknown>;
|
|
14
|
+
}
|
|
15
|
+
export interface TransportOptions {
|
|
16
|
+
token: string;
|
|
17
|
+
fetch?: typeof fetch;
|
|
18
|
+
signal?: AbortSignal | undefined;
|
|
19
|
+
/** The run's sink from `baseContext(...).run(events => …)`, so each request lands in the run record (WC-LIFE-15). */
|
|
20
|
+
events?: EventSink;
|
|
21
|
+
/** cli-core's per-profile pace, shared by every process of the profile through a file. */
|
|
22
|
+
pacer?: Pacer;
|
|
23
|
+
sleep?: SleepLike;
|
|
24
|
+
now?: () => number;
|
|
25
|
+
timeoutMs?: number;
|
|
26
|
+
}
|
|
27
|
+
/**
|
|
28
|
+
* A refusal as a CliError with cli-base's code for its status. The service's own message is never repeated: it can
|
|
29
|
+
* quote what was sent (WC-SECR-6). A write that failed on the service's side may have happened, so it is
|
|
30
|
+
* `outcome_unknown` and never retryable (WC-OUT-20, WC-LIFE-9).
|
|
31
|
+
*/
|
|
32
|
+
export declare const failureOf: (status: number, writes: boolean, retryAfterMs?: number) => CliError;
|
|
33
|
+
/**
|
|
34
|
+
* The only code that holds the token: it goes in the Authorization header, never in an address, an error or a run
|
|
35
|
+
* record (WC-SECR-7). Nothing here retries. A read the caller may run again; a write is never sent twice, since the
|
|
36
|
+
* first may have landed (WC-OUT-20).
|
|
37
|
+
*/
|
|
38
|
+
export declare const transport: (options: TransportOptions) => {
|
|
39
|
+
send: <T>(request: Request, answer: v.GenericSchema<unknown, T>) => Promise<T>;
|
|
40
|
+
};
|
|
41
|
+
export type Transport = ReturnType<typeof transport>;
|
|
42
|
+
//# sourceMappingURL=transport.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"transport.d.ts","sourceRoot":"","sources":["../../src/sample/transport.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,QAAQ,EAAyB,KAAK,SAAS,EAAa,MAAM,mBAAmB,CAAA;AAE9F,OAAO,EAAE,KAAK,SAAS,EAAmB,MAAM,uBAAuB,CAAA;AACvE,OAAO,KAAK,EAAE,KAAK,EAAE,MAAM,yBAAyB,CAAA;AACpD,OAAO,KAAK,CAAC,MAAM,SAAS,CAAA;AAI5B,eAAO,MAAM,WAAW,yCAAyC,CAAA;AACjE,eAAO,MAAM,OAAO,qBAAqB,CAAA;AAOzC,MAAM,WAAW,OAAO;IACtB,MAAM,EAAE,KAAK,GAAG,MAAM,GAAG,OAAO,CAAA;IAChC,+FAA+F;IAC/F,IAAI,EAAE,MAAM,CAAA;IACZ,GAAG,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAA;IAC5B,KAAK,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,GAAG,MAAM,GAAG,SAAS,CAAC,CAAA;IACnD,IAAI,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAA;CAC/B;AAED,MAAM,WAAW,gBAAgB;IAC/B,KAAK,EAAE,MAAM,CAAA;IACb,KAAK,CAAC,EAAE,OAAO,KAAK,CAAA;IACpB,MAAM,CAAC,EAAE,WAAW,GAAG,SAAS,CAAA;IAChC,qHAAqH;IACrH,MAAM,CAAC,EAAE,SAAS,CAAA;IAClB,0FAA0F;IAC1F,KAAK,CAAC,EAAE,KAAK,CAAA;IACb,KAAK,CAAC,EAAE,SAAS,CAAA;IACjB,GAAG,CAAC,EAAE,MAAM,MAAM,CAAA;IAClB,SAAS,CAAC,EAAE,MAAM,CAAA;CACnB;AAkBD;;;;GAIG;AACH,eAAO,MAAM,SAAS,WAAY,MAAM,UAAU,OAAO,iBAAiB,MAAM,KAAG,QAqBlF,CAAA;AAED;;;;GAIG;AACH,eAAO,MAAM,SAAS,YAAa,gBAAgB;WA6B7B,CAAC,WAAW,OAAO,UAAU,CAAC,CAAC,aAAa,CAAC,OAAO,EAAE,CAAC,CAAC,KAAG,OAAO,CAAC,CAAC,CAAC;CAiE1F,CAAA;AAED,MAAM,MAAM,SAAS,GAAG,UAAU,CAAC,OAAO,SAAS,CAAC,CAAA"}
|
|
@@ -0,0 +1,152 @@
|
|
|
1
|
+
import { CliError, isCliError, realSleep, wallClock } from "@wirecat/cli-base";
|
|
2
|
+
import { providerWaitMs, readJsonResponse, statusToCode } from "@wirecat/cli-base/http";
|
|
3
|
+
import { providerRequest } from "@wirecat/cli-core/cli";
|
|
4
|
+
import * as v from "valibot";
|
|
5
|
+
// SAMPLE: the Example Comments service does not exist; `.invalid` never resolves, so a real run reaches nothing.
|
|
6
|
+
// Replace the address, the paths and the answer shapes with your service's, each from its docs or a captured answer.
|
|
7
|
+
export const SERVICE_URL = "https://comments.example.invalid/v1/";
|
|
8
|
+
export const SERVICE = "Example Comments";
|
|
9
|
+
const TIMEOUT_MS = 30_000;
|
|
10
|
+
const LARGEST_ANSWER = 8 * 1024 * 1024;
|
|
11
|
+
/** A pace wait longer than this ends the run with `rate_limited` instead of sitting in it. */
|
|
12
|
+
const LONGEST_PACE_WAIT_MS = 60_000;
|
|
13
|
+
/** `new URL` folds a `.` or `..` segment into its parent, so such an id would reach another endpoint. */
|
|
14
|
+
const segment = (name, value) => {
|
|
15
|
+
if (value === "" || value === "." || value === "..")
|
|
16
|
+
throw new CliError("validation_error", `${name} must name one item; an empty, "." or ".." id is refused`);
|
|
17
|
+
return encodeURIComponent(value);
|
|
18
|
+
};
|
|
19
|
+
const urlOf = ({ path, ids = {}, query = {} }) => {
|
|
20
|
+
const url = new URL(path.replace(/\{(\w+)\}/g, (_, name) => segment(name, ids[name] ?? "")), SERVICE_URL);
|
|
21
|
+
for (const [key, value] of Object.entries(query))
|
|
22
|
+
if (value !== undefined)
|
|
23
|
+
url.searchParams.set(key, String(value));
|
|
24
|
+
return url;
|
|
25
|
+
};
|
|
26
|
+
/**
|
|
27
|
+
* A refusal as a CliError with cli-base's code for its status. The service's own message is never repeated: it can
|
|
28
|
+
* quote what was sent (WC-SECR-6). A write that failed on the service's side may have happened, so it is
|
|
29
|
+
* `outcome_unknown` and never retryable (WC-OUT-20, WC-LIFE-9).
|
|
30
|
+
*/
|
|
31
|
+
export const failureOf = (status, writes, retryAfterMs) => {
|
|
32
|
+
if (status === 401)
|
|
33
|
+
return new CliError("authentication_error", `${SERVICE} refused the token; sign in again with session start`, {
|
|
34
|
+
status,
|
|
35
|
+
});
|
|
36
|
+
if (status === 429)
|
|
37
|
+
return new CliError("rate_limited", `${SERVICE} asked to slow down; retry the command later`, {
|
|
38
|
+
status,
|
|
39
|
+
retryable: true,
|
|
40
|
+
...(retryAfterMs === undefined ? {} : { retry_after_ms: retryAfterMs }),
|
|
41
|
+
});
|
|
42
|
+
if (status >= 500 && writes)
|
|
43
|
+
return new CliError("outcome_unknown", `${SERVICE} failed during a write; check it before repeating the write`, {
|
|
44
|
+
status,
|
|
45
|
+
retryable: false,
|
|
46
|
+
});
|
|
47
|
+
if (status === 404)
|
|
48
|
+
return new CliError("not_found", `${SERVICE} has no such item, or this token cannot see it`);
|
|
49
|
+
return new CliError(statusToCode(status), `${SERVICE} refused the request (HTTP ${status})`, {
|
|
50
|
+
status,
|
|
51
|
+
retryable: status >= 500,
|
|
52
|
+
});
|
|
53
|
+
};
|
|
54
|
+
/**
|
|
55
|
+
* The only code that holds the token: it goes in the Authorization header, never in an address, an error or a run
|
|
56
|
+
* record (WC-SECR-7). Nothing here retries. A read the caller may run again; a write is never sent twice, since the
|
|
57
|
+
* first may have landed (WC-OUT-20).
|
|
58
|
+
*/
|
|
59
|
+
export const transport = (options) => {
|
|
60
|
+
const fetchOf = options.fetch ?? fetch;
|
|
61
|
+
const sleep = options.sleep ?? realSleep;
|
|
62
|
+
const now = options.now ?? Date.now;
|
|
63
|
+
const turn = async () => {
|
|
64
|
+
if (!options.pacer)
|
|
65
|
+
return;
|
|
66
|
+
const wait = (await options.pacer.reserve()) - now();
|
|
67
|
+
if (wait <= 0)
|
|
68
|
+
return;
|
|
69
|
+
if (wait > LONGEST_PACE_WAIT_MS)
|
|
70
|
+
throw new CliError("rate_limited", `this profile must wait ${Math.ceil(wait / 1000)} s before its next request`, {
|
|
71
|
+
retry_after_ms: wait,
|
|
72
|
+
});
|
|
73
|
+
await sleep(wait, options.signal, "retry");
|
|
74
|
+
};
|
|
75
|
+
const unanswered = (writes, error) => {
|
|
76
|
+
// Before the stop check: a write stopped in flight may still have landed (WC-LIFE-9).
|
|
77
|
+
if (writes)
|
|
78
|
+
return new CliError("outcome_unknown", `${SERVICE} did not answer a write; check it before repeating the write`, {
|
|
79
|
+
retryable: false,
|
|
80
|
+
});
|
|
81
|
+
if (options.signal?.aborted)
|
|
82
|
+
return new CliError("cancelled", `the ${SERVICE} request was stopped`);
|
|
83
|
+
const timedOut = error?.name === "TimeoutError";
|
|
84
|
+
return timedOut
|
|
85
|
+
? new CliError("timeout", `${SERVICE} did not answer in time`, { retryable: true })
|
|
86
|
+
: new CliError("network_error", `${SERVICE} could not be reached`, { retryable: true });
|
|
87
|
+
};
|
|
88
|
+
const send = async (request, answer) => {
|
|
89
|
+
const writes = request.method !== "GET";
|
|
90
|
+
const url = urlOf(request);
|
|
91
|
+
await turn();
|
|
92
|
+
// Not AbortSignal.timeout: its timer keeps the run's async context alive until it fires.
|
|
93
|
+
const deadline = new AbortController();
|
|
94
|
+
const timer = setTimeout(() => deadline.abort(new DOMException("timed out", "TimeoutError")), options.timeoutMs ?? TIMEOUT_MS);
|
|
95
|
+
timer.unref();
|
|
96
|
+
const signal = options.signal ? AbortSignal.any([options.signal, deadline.signal]) : deadline.signal;
|
|
97
|
+
const end = providerRequest(`${request.method} /${request.path}`, options.events, {
|
|
98
|
+
method: request.method,
|
|
99
|
+
transport: "http",
|
|
100
|
+
});
|
|
101
|
+
try {
|
|
102
|
+
let response;
|
|
103
|
+
try {
|
|
104
|
+
response = await fetchOf(url, {
|
|
105
|
+
method: request.method,
|
|
106
|
+
headers: {
|
|
107
|
+
authorization: `Bearer ${options.token}`,
|
|
108
|
+
accept: "application/json",
|
|
109
|
+
...(request.body ? { "content-type": "application/json" } : {}),
|
|
110
|
+
},
|
|
111
|
+
...(request.body ? { body: JSON.stringify(request.body) } : {}),
|
|
112
|
+
// A redirect would carry the Authorization header to an address we did not choose.
|
|
113
|
+
redirect: "error",
|
|
114
|
+
signal,
|
|
115
|
+
});
|
|
116
|
+
}
|
|
117
|
+
catch (error) {
|
|
118
|
+
const failure = unanswered(writes, deadline.signal.reason ?? error);
|
|
119
|
+
end({ errorCode: failure.code });
|
|
120
|
+
throw failure;
|
|
121
|
+
}
|
|
122
|
+
if (!response.ok) {
|
|
123
|
+
const failure = failureOf(response.status, writes, providerWaitMs(response.headers, wallClock));
|
|
124
|
+
end({ status: response.status, errorCode: failure.code });
|
|
125
|
+
throw failure;
|
|
126
|
+
}
|
|
127
|
+
let body;
|
|
128
|
+
try {
|
|
129
|
+
body = await readJsonResponse(response, { maxBytes: LARGEST_ANSWER, signal });
|
|
130
|
+
}
|
|
131
|
+
catch (error) {
|
|
132
|
+
const failure = isCliError(error) && error.code !== "invalid_response"
|
|
133
|
+
? error
|
|
134
|
+
: new CliError("invalid_response", `${SERVICE} answered with something other than bounded JSON`);
|
|
135
|
+
end({ status: response.status, errorCode: failure.code });
|
|
136
|
+
throw failure;
|
|
137
|
+
}
|
|
138
|
+
const parsed = v.safeParse(answer, body);
|
|
139
|
+
if (!parsed.success) {
|
|
140
|
+
end({ status: response.status, errorCode: "invalid_response" });
|
|
141
|
+
throw new CliError("invalid_response", `${SERVICE}'s answer does not have the documented shape`);
|
|
142
|
+
}
|
|
143
|
+
end({ status: response.status });
|
|
144
|
+
return parsed.output;
|
|
145
|
+
}
|
|
146
|
+
finally {
|
|
147
|
+
clearTimeout(timer);
|
|
148
|
+
}
|
|
149
|
+
};
|
|
150
|
+
return { send };
|
|
151
|
+
};
|
|
152
|
+
//# sourceMappingURL=transport.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"transport.js","sourceRoot":"","sources":["../../src/sample/transport.ts"],"names":[],"mappings":"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"}
|
package/dist/token.d.ts
ADDED
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
import { type CredentialSource, type KeyringStore } from "@wirecat/cli-base";
|
|
2
|
+
export declare const TOKEN_VARIABLE: string;
|
|
3
|
+
export type TokenStorage = "keyring" | "file";
|
|
4
|
+
/**
|
|
5
|
+
* One token per profile: `CLIDEV_TOKEN`, then the keyring entry `token:<profile>` under the app's name, then a 0600
|
|
6
|
+
* file (WC-SECR-4). A run with moved folders reads the owner's entry and never replaces or deletes it (WC-SECR-24);
|
|
7
|
+
* cli-base's `Credentials` does both. cli-core has no token store for a CLI that is not a bot; keep this one.
|
|
8
|
+
*/
|
|
9
|
+
export declare const tokenStore: (env: NodeJS.ProcessEnv, keyring: KeyringStore | undefined, warn: (message: string) => void) => {
|
|
10
|
+
read: (profile: string) => import("@wirecat/cli-base").StoredCredential | undefined;
|
|
11
|
+
write: (profile: string, token: string, storage: TokenStorage) => CredentialSource;
|
|
12
|
+
remove: (profile: string) => CredentialSource[];
|
|
13
|
+
};
|
|
14
|
+
/** Whether the profile has a token here, for `mcp check`, which prints its own verdict. */
|
|
15
|
+
export declare const holdsToken: (env: NodeJS.ProcessEnv, profile: string, keyring?: KeyringStore) => boolean;
|
|
16
|
+
//# sourceMappingURL=token.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"token.d.ts","sourceRoot":"","sources":["../src/token.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,KAAK,gBAAgB,EAA2B,KAAK,YAAY,EAAgB,MAAM,mBAAmB,CAAA;AAGnH,eAAO,MAAM,cAAc,QAAgC,CAAA;AAE3D,MAAM,MAAM,YAAY,GAAG,SAAS,GAAG,MAAM,CAAA;AAE7C;;;;GAIG;AACH,eAAO,MAAM,UAAU,QAChB,MAAM,CAAC,UAAU,WACb,YAAY,GAAG,SAAS,QAC3B,CAAC,OAAO,EAAE,MAAM,KAAK,IAAI;IAe7B,IAAI,YAAY,MAAM;IACtB,KAAK,YAAY,MAAM,SAAS,MAAM,WAAW,YAAY,KAAG,gBAAgB;IAIhF,MAAM,YAAY,MAAM,KAAG,gBAAgB,EAAE;CAEhD,CAAA;AAED,2FAA2F;AAC3F,eAAO,MAAM,UAAU,QAAS,MAAM,CAAC,UAAU,WAAW,MAAM,YAAY,YAAY,KAAG,OAC7B,CAAA"}
|
package/dist/token.js
ADDED
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
import { Credentials, isMovedRun, resolvePaths } from "@wirecat/cli-base";
|
|
2
|
+
import { IDENTITY } from "./identity.js";
|
|
3
|
+
export const TOKEN_VARIABLE = `${IDENTITY.envPrefix}_TOKEN`;
|
|
4
|
+
/**
|
|
5
|
+
* One token per profile: `CLIDEV_TOKEN`, then the keyring entry `token:<profile>` under the app's name, then a 0600
|
|
6
|
+
* file (WC-SECR-4). A run with moved folders reads the owner's entry and never replaces or deletes it (WC-SECR-24);
|
|
7
|
+
* cli-base's `Credentials` does both. cli-core has no token store for a CLI that is not a bot; keep this one.
|
|
8
|
+
*/
|
|
9
|
+
export const tokenStore = (env, keyring, warn) => {
|
|
10
|
+
const where = { appName: IDENTITY.appName, prefix: IDENTITY.envPrefix, env };
|
|
11
|
+
const options = {
|
|
12
|
+
configDir: resolvePaths(where).config,
|
|
13
|
+
service: IDENTITY.appName,
|
|
14
|
+
envVar: TOKEN_VARIABLE,
|
|
15
|
+
isolated: isMovedRun(where),
|
|
16
|
+
env,
|
|
17
|
+
...(keyring ? { keyring } : {}),
|
|
18
|
+
warn,
|
|
19
|
+
};
|
|
20
|
+
const credentials = new Credentials(options);
|
|
21
|
+
const account = (profile) => `token:${profile}`;
|
|
22
|
+
return {
|
|
23
|
+
read: (profile) => credentials.read(account(profile)),
|
|
24
|
+
write: (profile, token, storage) => new Credentials({ ...options, storage: storage === "file" ? "file" : "auto" }).write(account(profile), token, {
|
|
25
|
+
signIn: true,
|
|
26
|
+
}),
|
|
27
|
+
remove: (profile) => credentials.remove(account(profile)),
|
|
28
|
+
};
|
|
29
|
+
};
|
|
30
|
+
/** Whether the profile has a token here, for `mcp check`, which prints its own verdict. */
|
|
31
|
+
export const holdsToken = (env, profile, keyring) => tokenStore(env, keyring, () => { }).read(profile) !== undefined;
|
|
32
|
+
//# sourceMappingURL=token.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"token.js","sourceRoot":"","sources":["../src/token.ts"],"names":[],"mappings":"AAAA,OAAO,EAAyB,WAAW,EAAE,UAAU,EAAqB,YAAY,EAAE,MAAM,mBAAmB,CAAA;AACnH,OAAO,EAAE,QAAQ,EAAE,MAAM,eAAe,CAAA;AAExC,MAAM,CAAC,MAAM,cAAc,GAAG,GAAG,QAAQ,CAAC,SAAS,QAAQ,CAAA;AAI3D;;;;GAIG;AACH,MAAM,CAAC,MAAM,UAAU,GAAG,CACxB,GAAsB,EACtB,OAAiC,EACjC,IAA+B,EAC/B,EAAE;IACF,MAAM,KAAK,GAAG,EAAE,OAAO,EAAE,QAAQ,CAAC,OAAO,EAAE,MAAM,EAAE,QAAQ,CAAC,SAAS,EAAE,GAAG,EAAE,CAAA;IAC5E,MAAM,OAAO,GAAG;QACd,SAAS,EAAE,YAAY,CAAC,KAAK,CAAC,CAAC,MAAM;QACrC,OAAO,EAAE,QAAQ,CAAC,OAAO;QACzB,MAAM,EAAE,cAAc;QACtB,QAAQ,EAAE,UAAU,CAAC,KAAK,CAAC;QAC3B,GAAG;QACH,GAAG,CAAC,OAAO,CAAC,CAAC,CAAC,EAAE,OAAO,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;QAC/B,IAAI;KACL,CAAA;IACD,MAAM,WAAW,GAAG,IAAI,WAAW,CAAC,OAAO,CAAC,CAAA;IAC5C,MAAM,OAAO,GAAG,CAAC,OAAe,EAAE,EAAE,CAAC,SAAS,OAAO,EAAE,CAAA;IACvD,OAAO;QACL,IAAI,EAAE,CAAC,OAAe,EAAE,EAAE,CAAC,WAAW,CAAC,IAAI,CAAC,OAAO,CAAC,OAAO,CAAC,CAAC;QAC7D,KAAK,EAAE,CAAC,OAAe,EAAE,KAAa,EAAE,OAAqB,EAAoB,EAAE,CACjF,IAAI,WAAW,CAAC,EAAE,GAAG,OAAO,EAAE,OAAO,EAAE,OAAO,KAAK,MAAM,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,MAAM,EAAE,CAAC,CAAC,KAAK,CAAC,OAAO,CAAC,OAAO,CAAC,EAAE,KAAK,EAAE;YAC5G,MAAM,EAAE,IAAI;SACb,CAAC;QACJ,MAAM,EAAE,CAAC,OAAe,EAAsB,EAAE,CAAC,WAAW,CAAC,MAAM,CAAC,OAAO,CAAC,OAAO,CAAC,CAAC;KACtF,CAAA;AACH,CAAC,CAAA;AAED,2FAA2F;AAC3F,MAAM,CAAC,MAAM,UAAU,GAAG,CAAC,GAAsB,EAAE,OAAe,EAAE,OAAsB,EAAW,EAAE,CACrG,UAAU,CAAC,GAAG,EAAE,OAAO,EAAE,GAAG,EAAE,GAAE,CAAC,CAAC,CAAC,IAAI,CAAC,OAAO,CAAC,KAAK,SAAS,CAAA"}
|
package/dist/version.d.ts
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
export declare const VERSION = "0.
|
|
1
|
+
export declare const VERSION = "0.16.0";
|
|
2
2
|
//# sourceMappingURL=version.d.ts.map
|
package/dist/version.js
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
export const VERSION = "0.
|
|
1
|
+
export const VERSION = "0.16.0";
|
|
2
2
|
//# sourceMappingURL=version.js.map
|