apps 0.0.1-beta.32 → 0.0.1-beta.34

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.
Files changed (34) hide show
  1. package/framework-reference.json +1 -1
  2. package/js/{chunk-W7F23NQ2.js → chunk-5OM6HBNU.js} +11 -9
  3. package/js/{chunk-N72RKJ5E.js → chunk-DSUKW6KJ.js} +1 -92
  4. package/js/{chunk-5DLG2FVN.js → chunk-DXGWKNTQ.js} +91 -0
  5. package/js/chunk-EPUWF4ZG.js +49 -0
  6. package/js/{chunk-4OKYLVU4.js → chunk-IU6OK5G2.js} +1 -1
  7. package/js/{chunk-GG566OOE.js → chunk-MYH3QZ4L.js} +6 -6
  8. package/js/{chunk-NPGRURDG.js → chunk-O3OAFU7Q.js} +20 -3
  9. package/js/{chunk-RULZPNGP.js → chunk-O4QOPXQT.js} +8 -47
  10. package/js/{chunk-WG5DFZFT.js → chunk-PUBNIORL.js} +2 -2
  11. package/js/{chunk-AYM2FUI5.js → chunk-Q7Q5JTRW.js} +1 -1
  12. package/js/{chunk-3FJYVAE4.js → chunk-QKVZGWGZ.js} +5 -5
  13. package/js/{chunk-5XJR4XGW.js → chunk-QUEPZ67T.js} +6 -5
  14. package/js/{chunk-FULRJSWU.js → chunk-QZD2RZ4S.js} +6 -5
  15. package/js/{chunk-BGALSHLT.js → chunk-UKMIHP2G.js} +9 -6
  16. package/js/client.js +1 -1
  17. package/js/contracts.js +9 -9
  18. package/js/effect.js +10 -9
  19. package/js/graphql.js +14 -14
  20. package/js/host.js +10 -9
  21. package/js/index.js +8 -8
  22. package/js/mcp/effect.js +12 -11
  23. package/js/mcp/stdio.js +12 -11
  24. package/js/mcp.js +15 -14
  25. package/js/openapi.js +11 -9
  26. package/js/skills.js +110 -42
  27. package/package.json +1 -1
  28. package/runtime.json +1 -1
  29. package/types/apps/src/contracts/cache.d.ts +14 -0
  30. package/types/apps/src/contracts/skills.d.ts +16 -4
  31. package/types/apps/src/implementation/catalog-cache.d.ts +14 -17
  32. package/types/apps/src/implementation/schema.d.ts +5 -0
  33. package/types/apps/src/implementation/skills.d.ts +13 -19
  34. package/types/apps/src/skills.d.ts +4 -3
package/js/skills.js CHANGED
@@ -1,15 +1,24 @@
1
1
  import {
2
- catalogCache
3
- } from "./chunk-BGALSHLT.js";
4
- import "./chunk-AYM2FUI5.js";
2
+ catalogCache,
3
+ catalogScope
4
+ } from "./chunk-UKMIHP2G.js";
5
+ import "./chunk-Q7Q5JTRW.js";
6
+ import {
7
+ failOnNetworkRefusal
8
+ } from "./chunk-EPUWF4ZG.js";
5
9
  import {
6
10
  folderSkillsEffect,
7
11
  skillFromFiles
8
12
  } from "./chunk-OX233L7N.js";
9
13
  import {
14
+ accountProviderError,
10
15
  httpProviderError
11
- } from "./chunk-4OKYLVU4.js";
12
- import "./chunk-5DLG2FVN.js";
16
+ } from "./chunk-IU6OK5G2.js";
17
+ import {
18
+ NetworkRefused,
19
+ ProviderError,
20
+ networkRefusalStatus
21
+ } from "./chunk-DXGWKNTQ.js";
13
22
  import {
14
23
  AppSkillMetadata,
15
24
  AppSkillSource,
@@ -20,12 +29,13 @@ import {
20
29
  SkillServiceName,
21
30
  skillLoadLimits
22
31
  } from "./chunk-HW3DPBZR.js";
32
+ import "./chunk-ETLILMHY.js";
23
33
  import {
24
34
  FetchHttpClient_exports
25
35
  } from "./chunk-MFHUR5SB.js";
26
36
  import {
27
37
  wrap
28
- } from "./chunk-NPGRURDG.js";
38
+ } from "./chunk-O3OAFU7Q.js";
29
39
  import "./chunk-FBBXFFFJ.js";
30
40
  import "./chunk-QYM4STZW.js";
31
41
  import {
@@ -34,6 +44,7 @@ import {
34
44
  } from "./chunk-CJY5X26R.js";
35
45
  import {
36
46
  Effect_exports,
47
+ Encoding_exports,
37
48
  Ref_exports,
38
49
  Schema_exports,
39
50
  Stream_exports
@@ -600,6 +611,11 @@ var withService = (service) => (effect) => service === void 0 || !Schema_exports
600
611
  }) : error
601
612
  )
602
613
  );
614
+ var refusedRequest = (refused) => new SkillLoadFailed({
615
+ reason: "source",
616
+ status: networkRefusalStatus,
617
+ message: refused.message.slice(0, 500)
618
+ });
603
619
  var parse = (schema, input) => Schema_exports.decodeUnknownEffect(schema)(input).pipe(Effect_exports.mapError(() => failed("document")));
604
620
  var resourcePath = (path) => {
605
621
  if (!Schema_exports.is(SkillFilePath)(path)) return false;
@@ -615,23 +631,31 @@ var resourcePath = (path) => {
615
631
  var pathUrl = (base, path) => new URL(path.split("/").map(encodeURIComponent).join("/"), base).href;
616
632
  var reader = (transport) => Effect_exports.gen(function* () {
617
633
  const budget = yield* Ref_exports.make(0);
618
- const fetchBytes = (url, post) => Effect_exports.gen(function* () {
634
+ const fetchBytes = (url, request = {}) => Effect_exports.gen(function* () {
619
635
  const parsed = yield* Effect_exports.try({
620
636
  try: () => new URL(url),
621
637
  catch: () => failed("source")
622
638
  });
623
639
  if (!["https:", "http:"].includes(parsed.protocol) || parsed.username || parsed.password || parsed.hash)
624
640
  return yield* failed("source");
641
+ const refused = request.refused ?? refusedRequest;
625
642
  const client = HttpClient_exports.withScope(yield* HttpClient_exports.HttpClient);
626
643
  const headers = {
627
644
  "User-Agent": "executor-skills",
628
645
  Accept: "application/json, text/plain",
629
- "Cache-Control": "no-cache"
646
+ "Cache-Control": "no-cache",
647
+ ...request.headers
630
648
  };
631
- const response = yield* (post === void 0 ? client.get(parsed, { headers }) : client.post(parsed, {
632
- headers: { ...headers, ...post.headers },
633
- body: HttpBody_exports.uint8Array(post.body, post.headers["Content-Type"])
634
- })).pipe(Effect_exports.mapError(() => failed("request")));
649
+ const response = yield* (request.body === void 0 ? client.get(parsed, { headers }) : client.post(parsed, {
650
+ headers,
651
+ body: HttpBody_exports.uint8Array(request.body, request.headers?.["Content-Type"])
652
+ })).pipe(
653
+ // `ctx.fetch` rejects a request Executor's app network refused.
654
+ Effect_exports.mapError(
655
+ (error) => Schema_exports.is(NetworkRefused)(error.cause) ? refused(error.cause) : failed("request")
656
+ )
657
+ );
658
+ yield* failOnNetworkRefusal(response).pipe(Effect_exports.mapError(refused));
635
659
  if (response.status < 200 || response.status >= 300)
636
660
  return yield* rejected(response.status, response.headers);
637
661
  const chunks = [];
@@ -661,7 +685,7 @@ var reader = (transport) => Effect_exports.gen(function* () {
661
685
  Effect_exports.provideService(FetchHttpClient_exports.RequestInit, { redirect: "manual" }),
662
686
  Effect_exports.provideService(FetchHttpClient_exports.Fetch, transport.fetch ?? globalThis.fetch)
663
687
  );
664
- const read = (url) => fetchBytes(url).pipe(
688
+ const read = (url, request = {}) => fetchBytes(url, request).pipe(
665
689
  Effect_exports.flatMap(
666
690
  (result) => Effect_exports.try({
667
691
  try: () => new TextDecoder("utf-8", { fatal: true }).decode(result),
@@ -680,21 +704,40 @@ var git = (run) => Effect_exports.tryPromise({
680
704
  try: async () => run(),
681
705
  catch: (error) => error instanceof InflateLimitExceeded ? failed("limit") : unreadable()
682
706
  });
683
- var resolveCommit = (remote, repo, ref) => Effect_exports.gen(function* () {
707
+ var githubHosts = ["github.com", "raw.githubusercontent.com"];
708
+ var githubRequests = (remote, repo, token) => {
709
+ const refused = (host) => (refusal) => refusal.refusal.reason === "credential_host" ? new SkillLoadFailed({
710
+ reason: "source",
711
+ status: networkRefusalStatus,
712
+ message: `Executor did not send the GitHub token to ${host}. The account's provider must declare hosts ${githubHosts.join(" and ")}; reconnect an account connected with other hosts.`
713
+ }) : refusedRequest(refusal);
714
+ const upload = (body) => remote.fetchBytes(`https://${githubHosts[0]}/${repo}.git/git-upload-pack`, {
715
+ body,
716
+ headers: {
717
+ ...gitRequestHeaders,
718
+ ...token === void 0 ? {} : { Authorization: `Basic ${Encoding_exports.encodeBase64(`x-access-token:${token}`)}` }
719
+ },
720
+ refused: refused(githubHosts[0])
721
+ });
722
+ const file = (commit, path) => remote.read(pathUrl(`https://${githubHosts[1]}/${repo}/${commit}/`, path), {
723
+ ...token === void 0 ? {} : { headers: { Authorization: `token ${token}` } },
724
+ refused: refused(githubHosts[1])
725
+ });
726
+ return { repo, authenticated: token !== void 0, upload, file };
727
+ };
728
+ var resolveCommit = (github, ref) => Effect_exports.gen(function* () {
729
+ const { repo } = github;
684
730
  if (ref !== void 0 && /^[a-f0-9]{40}$/.test(ref)) return ref;
685
731
  const names = refCandidates(ref);
686
- const response = yield* remote.fetchBytes(`https://github.com/${repo}.git/git-upload-pack`, {
687
- body: lsRefsRequest(names),
688
- headers: gitRequestHeaders
689
- }).pipe(
690
- // GitHub asks for credentials when a repository is missing or private.
691
- Effect_exports.mapError(
692
- (error) => error.status === 401 || error.status === 404 ? new SkillLoadFailed({
732
+ const response = yield* github.upload(lsRefsRequest(names)).pipe(
733
+ Effect_exports.mapError((error) => {
734
+ const hidden = error.reason === "request" && (error.status === 404 || error.status === 401 && !github.authenticated);
735
+ return hidden ? new SkillLoadFailed({
693
736
  reason: "source",
694
- message: `GitHub has no public repository named ${repo}.`,
737
+ message: github.authenticated ? `GitHub has no repository named ${repo} that the account's token can read.` : `GitHub has no public repository named ${repo}. To read a private repository, pass a GitHub account and its token.`,
695
738
  status: error.status
696
- }) : error
697
- )
739
+ }) : error;
740
+ })
698
741
  );
699
742
  const refs = yield* git(() => parseLsRefs(response));
700
743
  const commit = names.map((name) => refs.get(name)).find((sha) => sha !== void 0);
@@ -705,10 +748,7 @@ var resolveCommit = (remote, repo, ref) => Effect_exports.gen(function* () {
705
748
  });
706
749
  return commit;
707
750
  });
708
- var listFiles = (remote, repo, commit, path) => remote.fetchBytes(`https://github.com/${repo}.git/git-upload-pack`, {
709
- body: treeFetchRequest(commit),
710
- headers: gitRequestHeaders
711
- }).pipe(
751
+ var listFiles = (github, commit, path) => github.upload(treeFetchRequest(commit)).pipe(
712
752
  Effect_exports.flatMap(
713
753
  (response) => git(
714
754
  () => parseTreeFetch(response, commit, path, { objectBytes: skillLoadLimits.fileBytes })
@@ -721,8 +761,8 @@ var SkillDirectories = Schema_exports.Array(
721
761
  files: Schema_exports.Array(Schema_exports.Struct({ path: Schema_exports.String, mode: Schema_exports.String }))
722
762
  })
723
763
  );
724
- var skillDirectories = (remote, repo, commit, path) => Effect_exports.gen(function* () {
725
- const files = yield* listFiles(remote, repo, commit, path);
764
+ var skillDirectories = (github, commit, path) => Effect_exports.gen(function* () {
765
+ const files = yield* listFiles(github, commit, path);
726
766
  const documents = files.filter(
727
767
  (file) => file.path === "SKILL.md" || file.path.endsWith("/SKILL.md")
728
768
  );
@@ -734,15 +774,21 @@ var skillDirectories = (remote, repo, commit, path) => Effect_exports.gen(functi
734
774
  return yield* failed("limit");
735
775
  return directories;
736
776
  });
737
- var cachedSkillDirectories = (cache, transport, repo, commit, path) => Effect_exports.tryPromise({
777
+ var cachedSkillDirectories = (cache, transport, options, commit) => Effect_exports.tryPromise({
738
778
  try: () => cache.get({
739
- key: ["apps/githubSkills/directories", 1, repo, commit, path ?? null],
779
+ key: ["apps/githubSkills/directories", 1, options.repo, commit, options.path ?? null],
740
780
  schema: wrap(SkillDirectories, false),
741
781
  freshFor: "7 days",
742
782
  load: (context) => Effect_exports.runPromise(
743
- // Keep the author's fetch, as every other read in this load does.
783
+ // Keep the author's fetch and this read's token, as every other request in it does.
744
784
  reader({ fetch: transport.fetch, signal: context.signal }).pipe(
745
- Effect_exports.flatMap((remote) => skillDirectories(remote, repo, commit, path))
785
+ Effect_exports.flatMap(
786
+ (remote) => skillDirectories(
787
+ githubRequests(remote, options.repo, options.token),
788
+ commit,
789
+ options.path
790
+ )
791
+ )
746
792
  ),
747
793
  { signal: context.signal }
748
794
  )
@@ -769,20 +815,42 @@ var cachedCatalog = (options, prefix, load) => options.cache === void 0 ? load(o
769
815
  })
770
816
  )
771
817
  );
818
+ var GitHubToken = Schema_exports.String.check(Schema_exports.isPattern(/^[\x21-\x7e]+$/));
819
+ var scopeMessages = {
820
+ "missing-account": "A GitHub token needs its account. Pass account: ctx.accounts.<slot> with it.",
821
+ "unselected-account": "The GitHub account is not one of this app's selected accounts."
822
+ };
823
+ var githubCache = (options) => options.account !== void 0 && !Schema_exports.is(GitHubToken)(options.token) ? Effect_exports.fail(
824
+ new SkillLoadFailed({
825
+ reason: "source",
826
+ message: "The GitHub account was passed without a valid token."
827
+ })
828
+ ) : catalogScope(
829
+ options,
830
+ options.token,
831
+ (problem) => new SkillLoadFailed({ reason: "source", message: scopeMessages[problem] })
832
+ );
833
+ var attributed = (account) => (error) => account !== void 0 && error.reason === "request" && (error.status === 401 || error.status === 403) ? accountProviderError(
834
+ new ProviderError({
835
+ reason: error.status === 401 ? "unauthorized" : "rejected",
836
+ status: error.status
837
+ }),
838
+ account.id
839
+ ) : error;
772
840
  var githubSkillsEffect = (options) => Effect_exports.gen(function* () {
773
841
  if (!/^[A-Za-z0-9_.-]+\/[A-Za-z0-9_.-]+$/.test(options.repo) || options.path !== void 0 && !resourcePath(options.path))
774
842
  return yield* failed("source");
843
+ const cache = yield* githubCache(options);
775
844
  return yield* cachedCatalog(
776
- options,
845
+ cache === void 0 ? options : { ...options, cache },
777
846
  ["apps/githubSkills/catalog", 1, options.repo, options.ref ?? null, options.path ?? null],
778
- (transport, cache) => githubCatalog(options, transport, cache)
847
+ (transport, cache2) => githubCatalog(options, transport, cache2)
779
848
  );
780
- }).pipe(withService("GitHub"));
849
+ }).pipe(withService("GitHub"), Effect_exports.mapError(attributed(options.account)));
781
850
  var githubCatalog = (options, transport, cache) => Effect_exports.gen(function* () {
782
- const remote = yield* reader(transport);
783
- const commit = yield* resolveCommit(remote, options.repo, options.ref);
784
- const resources = yield* cache === void 0 ? skillDirectories(remote, options.repo, commit, options.path) : cachedSkillDirectories(cache, transport, options.repo, commit, options.path);
785
- const base = `https://raw.githubusercontent.com/${options.repo}/${commit}/`;
851
+ const github = githubRequests(yield* reader(transport), options.repo, options.token);
852
+ const commit = yield* resolveCommit(github, options.ref);
853
+ const resources = yield* cache === void 0 ? skillDirectories(github, commit, options.path) : cachedSkillDirectories(cache, transport, options, commit);
786
854
  const skills = yield* Effect_exports.forEach(
787
855
  resources,
788
856
  ({ directory, files }) => Effect_exports.gen(function* () {
@@ -792,7 +860,7 @@ var githubCatalog = (options, transport, cache) => Effect_exports.gen(function*
792
860
  const path = file.path.slice(directory.length);
793
861
  if (!resourcePath(file.path) || !resourcePath(path) || file.mode === "120000")
794
862
  return yield* failed("source");
795
- return { path, content: yield* remote.read(pathUrl(base, file.path)) };
863
+ return { path, content: yield* github.file(commit, file.path) };
796
864
  }),
797
865
  { concurrency: skillLoadLimits.concurrency }
798
866
  );
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "apps",
3
- "version": "0.0.1-beta.32",
3
+ "version": "0.0.1-beta.34",
4
4
  "description": "TypeScript apps with typed tools, accounts, storage, and UI.",
5
5
  "type": "module",
6
6
  "sideEffects": false,