devframe 0.9.8 → 0.9.9

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 (116) hide show
  1. package/dist/{_shared-BM3PdYli.mjs → _shared-C08SdQRl.mjs} +15 -4
  2. package/dist/{_shared-mtXpcJIv.d.mts → _shared-CK3czkH4.d.mts} +25 -2
  3. package/dist/adapters/build.d.mts +6 -6
  4. package/dist/adapters/build.mjs +32 -22
  5. package/dist/adapters/cac.d.mts +16 -1
  6. package/dist/adapters/cac.mjs +8 -7
  7. package/dist/adapters/dev.d.mts +13 -13
  8. package/dist/adapters/dev.mjs +2 -2
  9. package/dist/adapters/embedded.d.mts +1 -1
  10. package/dist/adapters/initiate.d.mts +28 -26
  11. package/dist/adapters/initiate.mjs +1 -1
  12. package/dist/adapters/mcp.d.mts +28 -14
  13. package/dist/adapters/mcp.mjs +75 -39
  14. package/dist/{agent-tool-name-EgfoFO8C.mjs → agent-tool-name-DGzeuWcO.mjs} +2 -2
  15. package/dist/cli/main.d.mts +1 -1
  16. package/dist/cli/main.mjs +41 -17
  17. package/dist/client/index.d.mts +36 -36
  18. package/dist/client/index.mjs +83 -51
  19. package/dist/{client-assets-C7SXgTWY.mjs → client-assets-DNdhagGm.mjs} +2 -2
  20. package/dist/constants.d.mts +8 -7
  21. package/dist/constants.mjs +7 -6
  22. package/dist/{context-CLF4-8Pp.d.mts → context-4AIVRLYF.d.mts} +158 -122
  23. package/dist/{context-omlma_1n.mjs → context-CwPUinjn.mjs} +126 -88
  24. package/dist/{context-KUqAfawQ.mjs → context-DIUvJW4D.mjs} +3 -3
  25. package/dist/{context-DftYyXaR.d.mts → context-rgBSPFHS.d.mts} +5 -5
  26. package/dist/{crypto-token-XCqTSMg9.mjs → crypto-token-TVbGGFuj.mjs} +1 -1
  27. package/dist/{dev-CzkPCJwi.mjs → dev-Eqq5PX0k.mjs} +37 -18
  28. package/dist/{diagnostics-BXWW3VK-.mjs → diagnostics-BMfN8mb1.mjs} +30 -17
  29. package/dist/{diagnostics-CD8nlgll.mjs → diagnostics-D0EVm5M6.mjs} +2 -2
  30. package/dist/{events-4daw0uK9.mjs → events-BV4Dj59a.mjs} +2 -2
  31. package/dist/{host-h3-fRbF9yor.mjs → host-h3-BJ4siyz7.mjs} +1 -1
  32. package/dist/in-page-channel/index.d.mts +14 -14
  33. package/dist/in-page-channel/index.mjs +63 -54
  34. package/dist/{index-BzyopqxV.d.mts → index-pIVt92jP.d.mts} +9 -9
  35. package/dist/{index-D3k0BIhe.d.mts → index-pKl67H6w.d.mts} +4 -4
  36. package/dist/index.d.mts +8 -8
  37. package/dist/index.mjs +2 -2
  38. package/dist/{instance-registry-BjlMQbFB.mjs → instance-registry-C7-41RSp.mjs} +8 -8
  39. package/dist/{instance-shell-Tcp5hGoq.d.mts → instance-shell-7Z52PE4_.d.mts} +18 -18
  40. package/dist/{instance-shell-CpVW4zG0.mjs → instance-shell-cdVETpfz.mjs} +227 -58
  41. package/dist/internal/index.d.mts +40 -23
  42. package/dist/internal/index.mjs +10 -10
  43. package/dist/node/auth.d.mts +2 -2
  44. package/dist/node/auth.mjs +2 -2
  45. package/dist/node/hub-internals.d.mts +2 -2
  46. package/dist/node/hub-internals.mjs +2 -2
  47. package/dist/node/index.d.mts +5 -5
  48. package/dist/node/index.mjs +2 -2
  49. package/dist/{nostics-SPGDgHEP.d.mts → nostics-0sKe5HIj.d.mts} +2 -2
  50. package/dist/{nostics-CzECRXpE.mjs → nostics-D0PvLZsn.mjs} +1 -1
  51. package/dist/origin-DGDH8BI9.d.mts +45 -0
  52. package/dist/origin-DZit9g29.mjs +80 -0
  53. package/dist/recipes/common-rpc-functions.d.mts +5 -5
  54. package/dist/recipes/common-rpc-functions.mjs +3 -3
  55. package/dist/recipes/interactive-auth.d.mts +6 -6
  56. package/dist/recipes/interactive-auth.mjs +6 -6
  57. package/dist/{remote-assets-DezQmPQU.mjs → remote-assets-BIFWOlBW.mjs} +26 -6
  58. package/dist/{remote-assets-B13_vRkJ.d.mts → remote-assets-eBvSKZMS.d.mts} +7 -7
  59. package/dist/{revoke-COLW0cOi.mjs → revoke-cqztiCX4.mjs} +1 -1
  60. package/dist/rpc/dump.d.mts +2 -11
  61. package/dist/rpc/dump.mjs +1 -1
  62. package/dist/rpc/index.d.mts +2 -2
  63. package/dist/rpc/index.mjs +3 -3
  64. package/dist/rpc/transports/sse-client.d.mts +1 -1
  65. package/dist/rpc/transports/sse-client.mjs +30 -14
  66. package/dist/rpc/transports/sse-server.d.mts +4 -4
  67. package/dist/rpc/transports/sse-server.mjs +7 -7
  68. package/dist/rpc/transports/ws-bun.d.mts +5 -5
  69. package/dist/rpc/transports/ws-bun.mjs +8 -2
  70. package/dist/rpc/transports/ws-client.d.mts +1 -1
  71. package/dist/rpc/transports/ws-client.mjs +2 -2
  72. package/dist/rpc/transports/ws-deno.d.mts +4 -4
  73. package/dist/rpc/transports/ws-deno.mjs +8 -2
  74. package/dist/rpc/transports/ws-server.d.mts +2 -1
  75. package/dist/rpc/transports/ws-server.mjs +232 -1
  76. package/dist/{rpc-core-B3W3z9ff.d.mts → rpc-core-Ceu6AUz8.d.mts} +4 -4
  77. package/dist/{rpc-core-DlHd276T.mjs → rpc-core-DVQ7xi89.mjs} +14 -2
  78. package/dist/{serialization-BGzEwAdr.mjs → serialization-DTZnPQ1-.mjs} +20 -18
  79. package/dist/{services-install-CqqQVls5.mjs → services-install-P_DV7VYL.mjs} +32 -21
  80. package/dist/session-DPfj6zQG.mjs +16 -0
  81. package/dist/{shared-state--sduc6Xi.mjs → shared-state-CaxB1q0h.mjs} +2 -2
  82. package/dist/{simple-schema-DQPZrAaZ.mjs → simple-schema-Bi40l-P6.mjs} +2 -2
  83. package/dist/{sse-client-DwX021ST.d.mts → sse-client-BQ4jS3eO.d.mts} +4 -4
  84. package/dist/{state-CK9LjrnT.mjs → state-CeQQWzM-.mjs} +4 -4
  85. package/dist/{static-DV_qUSRS.mjs → static-DZ8P2Kn7.mjs} +130 -135
  86. package/dist/{storage-BXNVhKyR.mjs → storage-Dccf_IyD.mjs} +1 -1
  87. package/dist/types/index.d.mts +6 -6
  88. package/dist/{types-CMdPYhOx.d.mts → types-Dq4i156u.d.mts} +17 -17
  89. package/dist/utils/agent-tool-name.d.mts +2 -2
  90. package/dist/utils/agent-tool-name.mjs +2 -2
  91. package/dist/utils/colors.d.mts +1 -1
  92. package/dist/utils/crypto-token.d.mts +1 -1
  93. package/dist/utils/crypto-token.mjs +1 -1
  94. package/dist/utils/events.d.mts +1 -1
  95. package/dist/utils/nostics.d.mts +1 -1
  96. package/dist/utils/nostics.mjs +1 -1
  97. package/dist/utils/open.mjs +1 -1
  98. package/dist/utils/origin.d.mts +2 -0
  99. package/dist/utils/origin.mjs +80 -0
  100. package/dist/utils/remote-assets.d.mts +2 -2
  101. package/dist/utils/remote-assets.mjs +1 -1
  102. package/dist/utils/serve-static.d.mts +4 -4
  103. package/dist/utils/serve-static.mjs +56 -19
  104. package/dist/utils/shared-state.d.mts +1 -1
  105. package/dist/utils/shared-state.mjs +1 -1
  106. package/dist/utils/simple-schema.d.mts +5 -5
  107. package/dist/utils/simple-schema.mjs +2 -2
  108. package/dist/utils/streaming-channel.d.mts +1 -1
  109. package/dist/utils/streaming-channel.mjs +2 -2
  110. package/dist/utils/when.d.mts +5 -5
  111. package/dist/{validation-YrZH6atx.mjs → validation-BOy8IoFZ.mjs} +2 -2
  112. package/dist/{wire-codec-0K-o5MYW.mjs → wire-codec-dcCeXcSo.mjs} +3 -3
  113. package/dist/{ws-client-7_lZi-B9.d.mts → ws-client-Xpxt0oPA.d.mts} +3 -3
  114. package/dist/{ws-server-VyHlanzy.d.mts → ws-server-D2nv68Hg.d.mts} +13 -37
  115. package/package.json +2 -1
  116. package/dist/ws-server-Y_AumUQH.mjs +0 -294
@@ -1,13 +1,13 @@
1
- import { i as defineDiagnostics } from "./nostics-CzECRXpE.mjs";
2
- import { t as diagnostics } from "./diagnostics-CD8nlgll.mjs";
1
+ import { i as defineDiagnostics } from "./nostics-D0PvLZsn.mjs";
2
+ import { t as diagnostics } from "./diagnostics-D0EVm5M6.mjs";
3
3
  import { RpcFunctionsCollectorBase } from "./rpc/index.mjs";
4
4
  import { defineRpcFunction } from "./index.mjs";
5
- import { t as DEVFRAME_EVENTS } from "./events-4daw0uK9.mjs";
5
+ import { t as DEVFRAME_EVENTS } from "./events-BV4Dj59a.mjs";
6
6
  import { DEVFRAME_SERVICES_STATE_KEY } from "./constants.mjs";
7
- import { t as diagnostics$1 } from "./diagnostics-BXWW3VK-.mjs";
8
- import { a as DevframeAgentHost, i as satisfiesVersionRange, n as expandResolveFrom, r as importServicePackage, s as createEventEmitter, t as deepMergeOptionSets } from "./services-install-CqqQVls5.mjs";
9
- import { n as createDebug, t as resolveStaticAssetsSource } from "./remote-assets-DezQmPQU.mjs";
10
- import { t as createStorage } from "./storage-BXNVhKyR.mjs";
7
+ import { t as diagnostics$1 } from "./diagnostics-BMfN8mb1.mjs";
8
+ import { a as DevframeAgentHost, i as satisfiesVersionRange, n as expandResolveFrom, r as importServicePackage, s as createEventEmitter, t as deepMergeOptionSets } from "./services-install-P_DV7VYL.mjs";
9
+ import { n as createDebug, t as resolveStaticAssetsSource } from "./remote-assets-BIFWOlBW.mjs";
10
+ import { t as createStorage } from "./storage-Dccf_IyD.mjs";
11
11
  import { createSharedState } from "devframe/utils/shared-state";
12
12
  import process from "node:process";
13
13
  import { join } from "pathe";
@@ -17,6 +17,10 @@ var DevframeDiagnosticsHost = class {
17
17
  context;
18
18
  _registry = {};
19
19
  logger = new Proxy({}, { get: (_, code) => this._registry[code] });
20
+ /**
21
+ * Already pre-wires devframe's ANSI console reporter, so no extra merging
22
+ * needed here, the host's `defineDiagnostics` just is the shared one.
23
+ */
20
24
  defineDiagnostics = defineDiagnostics;
21
25
  constructor(context, initialDefinitions = []) {
22
26
  this.context = context;
@@ -123,6 +127,10 @@ function createRpcSharedStateServerHost(rpc) {
123
127
  if (!sharedState.has(key)) return void 0;
124
128
  return (await host.get(key)).value();
125
129
  },
130
+ /**
131
+ * Pre-compute snapshots for the build-mode static dump so the SPA
132
+ * can read them without a live server.
133
+ */
126
134
  dump: () => ({ inputs: host.keys().map((key) => [key]) })
127
135
  });
128
136
  rpc.register({
@@ -158,7 +166,7 @@ var StreamClosedError = class extends Error {
158
166
  name = "StreamClosedError";
159
167
  };
160
168
  /**
161
- * Build a server-side stream sink. RPC-agnostic — the RPC host wires
169
+ * Build a server-side stream sink. RPC-agnostic; the RPC host wires
162
170
  * `events.on('chunk' | 'end')` to broadcast, and reads `buffer` to replay
163
171
  * for late or reconnecting subscribers.
164
172
  */
@@ -229,7 +237,7 @@ function createStreamSink(options = {}) {
229
237
  };
230
238
  }
231
239
  /**
232
- * Build a client-side stream reader. RPC-agnostic — the RPC host calls
240
+ * Build a client-side stream reader. RPC-agnostic; the RPC host calls
233
241
  * `_push(seq, chunk)` on each incoming chunk and `_end(error?)` on the
234
242
  * terminal frame. Consumers iterate with `for await` or pipe `readable`.
235
243
  */
@@ -412,7 +420,7 @@ function streamKey(channel, id) {
412
420
  }
413
421
  /**
414
422
  * Build the server-side streaming host. Mirrors the layout of
415
- * `createRpcSharedStateServerHost` — registers a fixed set of internal
423
+ * `createRpcSharedStateServerHost`: registers a fixed set of internal
416
424
  * RPC methods (`subscribe` / `unsubscribe` / `cancel`) once, then per-channel
417
425
  * state lives in a `Map<channelName, ChannelState>`.
418
426
  */
@@ -573,6 +581,10 @@ function createRpcStreamingServerHost(rpc) {
573
581
  name,
574
582
  options: {
575
583
  replayWindow,
584
+ /**
585
+ * Default to a 30-second hold when replay is enabled so late
586
+ * subscribers can join after the producer finishes.
587
+ */
576
588
  closedStreamRetention: opts.closedStreamRetention ?? (replayWindow > 0 ? 3e4 : 0)
577
589
  },
578
590
  streams: /* @__PURE__ */ new Map(),
@@ -669,37 +681,39 @@ function createRpcStreamingServerHost(rpc) {
669
681
  id: key.slice(sepIdx + 1)
670
682
  };
671
683
  }
684
+ function dropOutboundSubscriptions(meta) {
685
+ if (!meta.subscribedStreams) return;
686
+ for (const key of meta.subscribedStreams) {
687
+ const parsed = parseKey(key);
688
+ const state = parsed && channels.get(parsed.channelName);
689
+ const record = state && parsed && state.streams.get(parsed.id);
690
+ if (!parsed || !state || !record) continue;
691
+ record.subscribers.delete(meta);
692
+ if (record.subscribers.size === 0 && !record.sink.closed) record.sink.abort("all subscribers disconnected");
693
+ maybeFreeStream(state, parsed.id);
694
+ }
695
+ meta.subscribedStreams.clear();
696
+ }
697
+ function endInboundUploads(meta) {
698
+ if (!meta.uploadingStreams) return;
699
+ for (const key of meta.uploadingStreams) {
700
+ const parsed = parseKey(key);
701
+ const state = parsed && channels.get(parsed.channelName);
702
+ const record = state && parsed && state.inbound.get(parsed.id);
703
+ if (!parsed || !state || !record) continue;
704
+ record.reader._end({
705
+ name: "UploadDisconnected",
706
+ message: "Uploader disconnected before completing the stream"
707
+ });
708
+ state.inbound.delete(parsed.id);
709
+ }
710
+ meta.uploadingStreams.clear();
711
+ }
672
712
  return {
673
713
  create: createChannel,
674
714
  _onSessionDisconnected(meta) {
675
- if (meta.subscribedStreams) {
676
- for (const key of meta.subscribedStreams) {
677
- const parsed = parseKey(key);
678
- if (!parsed) continue;
679
- const state = channels.get(parsed.channelName);
680
- const record = state?.streams.get(parsed.id);
681
- if (!state || !record) continue;
682
- record.subscribers.delete(meta);
683
- if (record.subscribers.size === 0 && !record.sink.closed) record.sink.abort("all subscribers disconnected");
684
- maybeFreeStream(state, parsed.id);
685
- }
686
- meta.subscribedStreams.clear();
687
- }
688
- if (meta.uploadingStreams) {
689
- for (const key of meta.uploadingStreams) {
690
- const parsed = parseKey(key);
691
- if (!parsed) continue;
692
- const state = channels.get(parsed.channelName);
693
- const record = state?.inbound.get(parsed.id);
694
- if (!state || !record) continue;
695
- record.reader._end({
696
- name: "UploadDisconnected",
697
- message: "Uploader disconnected before completing the stream"
698
- });
699
- state.inbound.delete(parsed.id);
700
- }
701
- meta.uploadingStreams.clear();
702
- }
715
+ dropOutboundSubscriptions(meta);
716
+ endInboundUploads(meta);
703
717
  }
704
718
  };
705
719
  }
@@ -784,13 +798,24 @@ function validateServiceDefinition(def) {
784
798
  });
785
799
  }
786
800
  /**
801
+ * Merge every installer's option set in declaration order (the default
802
+ * deep-merge unions arrays and lets later scalars win; a service may override
803
+ * with its own `mergeOptions`).
804
+ */
805
+ function mergeInstallerOptions(def, entries) {
806
+ const sets = entries.map((entry) => entry.input.options).filter((options) => options !== void 0);
807
+ if (def.mergeOptions) return def.mergeOptions(sets);
808
+ return sets.length > 0 ? deepMergeOptionSets(sets) : void 0;
809
+ }
810
+ /**
787
811
  * Cross-plugin service registry (see `types/services.ts` for the contract).
788
812
  * Values are held per context instance; `whenAvailable` subscriptions make
789
- * the mechanism robust against setup ordering between provider and consumer.
813
+ * the mechanism work regardless of setup ordering between provider and
814
+ * consumer.
790
815
  *
791
816
  * On top of the in-process `provide`/`get` tier, this host implements the
792
817
  * **wire-service** lifecycle: `install()` queues definitions/descriptors,
793
- * `ready()` fires the collect-then-setup barrier — importing descriptor
818
+ * `ready()` fires the collect-then-setup barrier: importing descriptor
794
819
  * packages, merging option sets per service, constructing each service once,
795
820
  * providing its node API under the package name, and advertising it to
796
821
  * clients through the `devframe:services` shared state.
@@ -887,42 +912,68 @@ var DevframeServicesHostImpl = class {
887
912
  }
888
913
  let def = entries.filter((entry) => isServiceDefinition(entry.input))[0]?.input;
889
914
  if (!def) {
890
- const required = entries.map((entry) => entry.input).some((descriptor) => descriptor.required === true);
891
- const cwd = this.context?.cwd ?? process.cwd();
892
- const resolveFroms = [
893
- ...entries.map((entry) => entry.resolveFrom && expandResolveFrom(entry.resolveFrom, cwd)),
894
- this.context?.workspaceRoot,
895
- cwd
896
- ];
897
- let mod;
898
- try {
899
- mod = await importServicePackage(pkg, resolveFroms);
900
- } catch (error) {
901
- const reason = error instanceof Error ? error.message : String(error);
902
- if (required) throw diagnostics$1.DF0067({
903
- package: pkg,
904
- reason,
905
- cause: error
906
- });
907
- debug("optional service %s not importable, skipping: %s", pkg, reason);
908
- return;
909
- }
910
- const factory = mod.default;
911
- if (typeof factory !== "function") throw diagnostics$1.DF0070({
912
- package: pkg,
913
- reason: "its default export is not a factory function"
914
- });
915
- def = await factory();
916
- if (!def || typeof def.setup !== "function") throw diagnostics$1.DF0070({
915
+ def = await this.importServiceDefinition(pkg, entries);
916
+ if (!def) return void 0;
917
+ }
918
+ this.checkVersionRanges(pkg, def, entries);
919
+ const options = mergeInstallerOptions(def, entries);
920
+ if (!this.context) throw diagnostics$1.DF0070({
921
+ package: pkg,
922
+ reason: "this services host has no node context to install into"
923
+ });
924
+ debug("installing service %s@%s (scope %s)", def.package, def.version, def.scope);
925
+ const scoped = this.context.scope(def.scope);
926
+ const api = await def.setup(scoped, options === void 0 ? {} : { options });
927
+ this.installed.set(def.package, api);
928
+ this.provide(def.package, api);
929
+ await this.advertise(def);
930
+ return api;
931
+ }
932
+ /**
933
+ * Import a descriptor-only package, run its factory, and validate the
934
+ * resulting definition. Returns `undefined` when an optional package can't
935
+ * be imported (the install is skipped); throws for a required one.
936
+ */
937
+ async importServiceDefinition(pkg, entries) {
938
+ const required = entries.map((entry) => entry.input).some((descriptor) => descriptor.required === true);
939
+ const cwd = this.context?.cwd ?? process.cwd();
940
+ const resolveFroms = [
941
+ ...entries.map((entry) => entry.resolveFrom && expandResolveFrom(entry.resolveFrom, cwd)),
942
+ this.context?.workspaceRoot,
943
+ cwd
944
+ ];
945
+ let mod;
946
+ try {
947
+ mod = await importServicePackage(pkg, resolveFroms);
948
+ } catch (error) {
949
+ const reason = error instanceof Error ? error.message : String(error);
950
+ if (required) throw diagnostics$1.DF0067({
917
951
  package: pkg,
918
- reason: "its factory did not return a definition with a `setup` function"
952
+ reason,
953
+ cause: error
919
954
  });
920
- if (typeof def.package !== "string" || def.package.length === 0) def = {
921
- ...def,
922
- package: pkg
923
- };
924
- validateServiceDefinition(def);
955
+ debug("optional service %s not importable, skipping: %s", pkg, reason);
956
+ return;
925
957
  }
958
+ const factory = mod.default;
959
+ if (typeof factory !== "function") throw diagnostics$1.DF0070({
960
+ package: pkg,
961
+ reason: "its default export is not a factory function"
962
+ });
963
+ let def = await factory();
964
+ if (!def || typeof def.setup !== "function") throw diagnostics$1.DF0070({
965
+ package: pkg,
966
+ reason: "its factory did not return a definition with a `setup` function"
967
+ });
968
+ if (typeof def.package !== "string" || def.package.length === 0) def = {
969
+ ...def,
970
+ package: pkg
971
+ };
972
+ validateServiceDefinition(def);
973
+ return def;
974
+ }
975
+ /** Version-range checks against the resolved definition's real version. */
976
+ checkVersionRanges(pkg, def, entries) {
926
977
  for (const entry of entries) {
927
978
  const descriptor = entry.input;
928
979
  if (isServiceDefinition(entry.input) || typeof descriptor.version !== "string") continue;
@@ -938,19 +989,6 @@ var DevframeServicesHostImpl = class {
938
989
  installed: def.version
939
990
  });
940
991
  }
941
- const sets = entries.map((entry) => entry.input.options).filter((options) => options !== void 0);
942
- const options = def.mergeOptions ? def.mergeOptions(sets) : sets.length > 0 ? deepMergeOptionSets(sets) : void 0;
943
- if (!this.context) throw diagnostics$1.DF0070({
944
- package: pkg,
945
- reason: "this services host has no node context to install into"
946
- });
947
- debug("installing service %s@%s (scope %s)", def.package, def.version, def.scope);
948
- const scoped = this.context.scope(def.scope);
949
- const api = await def.setup(scoped, options === void 0 ? {} : { options });
950
- this.installed.set(def.package, api);
951
- this.provide(def.package, api);
952
- await this.advertise(def);
953
- return api;
954
992
  }
955
993
  advertisementState() {
956
994
  return this.context.rpc.sharedState.get(DEVFRAME_SERVICES_STATE_KEY, { initialValue: {} });
@@ -1019,7 +1057,7 @@ const agentListResources = defineRpcFunction({
1019
1057
  /**
1020
1058
  * Built-in agent introspection RPC functions. Registered automatically
1021
1059
  * by `createHostContext`. Not themselves agent-exposed (no `agent`
1022
- * field) — they power the MCP adapter and any future agent CLI.
1060
+ * field); they power the MCP adapter and any future agent CLI.
1023
1061
  */
1024
1062
  const BUILTIN_AGENT_RPC = [
1025
1063
  defineRpcFunction({
@@ -1169,7 +1207,7 @@ function createScopedNodeContext(context, namespace) {
1169
1207
  * Framework- and build-tool-agnostic core of the Devframe node context.
1170
1208
  * Wires the RPC host, view (HTTP file-serving) host, diagnostics, and
1171
1209
  * agent subsystems. Host adapters can wrap this to augment `ctx` with
1172
- * extra surfaces — for example, `@vitejs/devtools-kit`'s
1210
+ * extra surfaces; for example, `@vitejs/devtools-kit`'s
1173
1211
  * `createKitContext` attaches `docks`, `terminals`, `messages`, and
1174
1212
  * `commands` when mounted into Vite DevTools.
1175
1213
  */
@@ -1,6 +1,6 @@
1
- import { n as randomToken } from "./crypto-token-XCqTSMg9.mjs";
2
- import { t as createStorage } from "./storage-BXNVhKyR.mjs";
3
- import { n as revokeAuthToken, t as revokeActiveConnectionsForToken } from "./revoke-COLW0cOi.mjs";
1
+ import { n as randomToken } from "./crypto-token-TVbGGFuj.mjs";
2
+ import { t as createStorage } from "./storage-Dccf_IyD.mjs";
3
+ import { n as revokeAuthToken, t as revokeActiveConnectionsForToken } from "./revoke-cqztiCX4.mjs";
4
4
  import { join } from "pathe";
5
5
  //#region src/node/hub-internals/context.ts
6
6
  const internalContextMap = /* @__PURE__ */ new WeakMap();
@@ -1,4 +1,4 @@
1
- import { k as SharedState, o as DevframeNodeContext } from "./context-CLF4-8Pp.mjs";
1
+ import { k as SharedState, o as DevframeNodeContext } from "./context-4AIVRLYF.mjs";
2
2
  //#region src/node/hub-internals/context.d.ts
3
3
  interface InternalAnonymousAuthStorage {
4
4
  trusted: Record<string, {
@@ -10,7 +10,7 @@ interface InternalAnonymousAuthStorage {
10
10
  }
11
11
  interface RemoteTokenRecord {
12
12
  dockId: string;
13
- /** Dock URL origin — matched against WS handshake `Origin` header when `originLock` is on. */
13
+ /** Dock URL origin, matched against WS handshake `Origin` header when `originLock` is on. */
14
14
  origin: string;
15
15
  originLock: boolean;
16
16
  }
@@ -24,7 +24,7 @@ interface DevframeInternalContext {
24
24
  */
25
25
  revokeAuthToken: (token: string) => Promise<void>;
26
26
  /**
27
- * Session-only tokens issued to remote-UI iframe docks. Not persisted —
27
+ * Session-only tokens issued to remote-UI iframe docks. Not persisted;
28
28
  * regenerated on every dev-server restart.
29
29
  */
30
30
  remoteTokens: Map<string, RemoteTokenRecord>;
@@ -45,8 +45,8 @@ interface DevframeInternalContext {
45
45
  url: string;
46
46
  };
47
47
  /**
48
- * Set {@link DevframeInternalContext.wsEndpoint} and notify subscribers —
49
- * the WS-binding tiers (side-car, shared-server, and the `unbound` tier's
48
+ * Set {@link DevframeInternalContext.wsEndpoint} and notify subscribers.
49
+ * The WS-binding tiers (side-car, shared-server, and the `unbound` tier's
50
50
  * `attach()`) call this once the socket is bound (or `undefined` once torn
51
51
  * down) instead of assigning the field directly, so anything that already
52
52
  * projected the endpoint (a hub's remote-dock URLs, registered before an
@@ -2,7 +2,7 @@
2
2
  const HEX = "0123456789abcdef";
3
3
  /**
4
4
  * Generate a high-entropy, URL-safe (hex) random token suitable for use as a
5
- * bearer credential — e.g. the persistent client auth token or an ephemeral
5
+ * bearer credential, e.g. the persistent client auth token or an ephemeral
6
6
  * remote-dock token. Defaults to 16 bytes (128 bits) of entropy.
7
7
  */
8
8
  function randomToken(byteLength = 16) {
@@ -1,17 +1,17 @@
1
- import { t as resolveClientAssets } from "./client-assets-C7SXgTWY.mjs";
1
+ import { t as resolveClientAssets } from "./client-assets-DNdhagGm.mjs";
2
2
  import { DEVFRAME_CONNECTION_META_FILENAME } from "./constants.mjs";
3
- import { t as diagnostics } from "./diagnostics-BXWW3VK-.mjs";
4
- import { t as createHostContext } from "./context-omlma_1n.mjs";
5
- import { t as resolveStaticAssetsSource } from "./remote-assets-DezQmPQU.mjs";
6
- import { i as resolveMcpConnectionMeta, n as resolveBasePath, r as resolveDevServerPort, t as normalizeBasePath } from "./_shared-BM3PdYli.mjs";
7
- import { t as createH3DevframeHost } from "./host-h3-fRbF9yor.mjs";
8
- import { a as importRuntimeModule, i as normalizeHttpServerUrl, n as resolveInstanceRegister, t as createInstanceShell } from "./instance-shell-CpVW4zG0.mjs";
3
+ import { t as diagnostics } from "./diagnostics-BMfN8mb1.mjs";
4
+ import { t as createHostContext } from "./context-CwPUinjn.mjs";
5
+ import { t as resolveStaticAssetsSource } from "./remote-assets-BIFWOlBW.mjs";
6
+ import { i as resolveMcpConfig, n as resolveBasePath, r as resolveDevServerPort, t as normalizeBasePath } from "./_shared-C08SdQRl.mjs";
7
+ import { t as createH3DevframeHost } from "./host-h3-BJ4siyz7.mjs";
8
+ import { a as importRuntimeModule, i as normalizeHttpServerUrl, n as resolveInstanceRegister, t as createInstanceShell } from "./instance-shell-cdVETpfz.mjs";
9
9
  import { open } from "./utils/open.mjs";
10
10
  import { mountStaticHandler } from "./utils/serve-static.mjs";
11
11
  import { createServer } from "node:http";
12
12
  import process from "node:process";
13
13
  import { resolve } from "pathe";
14
- import { joinURL, withBase } from "ufo";
14
+ import { joinURL, withBase, withoutLeadingSlash } from "ufo";
15
15
  import { H3, toNodeHandler } from "h3";
16
16
  //#region src/adapters/initiate.ts
17
17
  const INSTANCE_INTERNALS = /* @__PURE__ */ new WeakMap();
@@ -20,7 +20,7 @@ function getInstanceInternals(handler) {
20
20
  return INSTANCE_INTERNALS.get(handler) ?? {};
21
21
  }
22
22
  /**
23
- * Serve a devframe through one framework-agnostic, web-standard handler —
23
+ * Serve a devframe through one framework-agnostic, web-standard handler:
24
24
  * the SPA, `__connection.json` discovery, the WebSocket RPC endpoint, the
25
25
  * auth gate, and the optional MCP route, all under a single mount base.
26
26
  * Mount `handler` on any framework's catch-all route (or `nodeMiddleware` on
@@ -28,10 +28,10 @@ function getInstanceInternals(handler) {
28
28
  *
29
29
  * The factory is synchronous and kicks off initialization eagerly;
30
30
  * `handler`/`nodeMiddleware` await readiness internally. Nothing binds a port
31
- * on its own: the WebSocket resolves in precedence order — `ws.port` (pinned
31
+ * on its own: the WebSocket resolves in precedence order: `ws.port` (pinned
32
32
  * side-car) > `server` (shared upgrade at `<base>__ws`) > `ws.sidecar`
33
33
  * (auto-port side-car) > the host driving upgrades itself through
34
- * {@link DevframeInstance.attach} — while `ws.url`, when set, overrides the
34
+ * {@link DevframeInstance.attach}. Meanwhile `ws.url`, when set, overrides the
35
35
  * advertised* endpoint (the tunnel pattern) and on its own hands the whole
36
36
  * transport to an external server. `__connection.json` reflects whichever
37
37
  * combination is active.
@@ -46,6 +46,10 @@ function initDevframe(def, options) {
46
46
  app,
47
47
  host,
48
48
  origin: options.origin,
49
+ /**
50
+ * Auth resolution mirrors `createDevServer`: gate by default, explicit
51
+ * `false` opts out, a handler object installs a custom scheme.
52
+ */
49
53
  auth: options.auth !== void 0 ? options.auth : def.cli?.auth,
50
54
  server: options.server,
51
55
  ws: options.ws ?? def.cli?.ws,
@@ -84,12 +88,13 @@ function initDevframe(def, options) {
84
88
  for (const input of def.services ?? []) context.services.install(input, { resolveFrom: def.importMetaUrl });
85
89
  await context.services.ready();
86
90
  await def.setup(context, setupInfo);
87
- const mcpOption = options.mcp ?? def.cli?.mcp;
88
- const mcpMeta = resolveMcpConnectionMeta(def, mcpOption);
91
+ const mcpConfig = resolveMcpConfig(options.mcp ?? def.cli?.mcp);
92
+ let mcpMeta;
89
93
  let mcpDispose;
90
- if (mcpMeta) {
91
- const mcpConfig = mcpOption === true || mcpOption === void 0 ? {} : mcpOption;
92
- const mcpPath = joinURL(base, mcpMeta.path);
94
+ if (mcpConfig) {
95
+ const mcpRoute = withoutLeadingSlash(mcpConfig.path ?? "__mcp");
96
+ mcpMeta = { path: mcpRoute };
97
+ const mcpPath = joinURL(base, mcpRoute);
93
98
  let mountMcpHttp;
94
99
  try {
95
100
  ({mountMcpHttp} = await importRuntimeModule("devframe/adapters/mcp"));
@@ -105,6 +110,7 @@ function initDevframe(def, options) {
105
110
  serverName: `${def.id} (devframe)`,
106
111
  serverVersion: def.version ?? "0.0.0",
107
112
  exposeSharedState: true,
113
+ authorization: mcpConfig.authorization,
108
114
  allowedOrigins: mcpConfig.allowedOrigins
109
115
  }).dispose;
110
116
  }
@@ -139,14 +145,14 @@ function initDevframe(def, options) {
139
145
  //#endregion
140
146
  //#region src/adapters/dev.ts
141
147
  /**
142
- * Start a devframe dev server for a {@link DevframeDefinition} —
148
+ * Start a devframe dev server for a {@link DevframeDefinition}:
143
149
  * h3 + WebSocket RPC + (optionally) the author's SPA mounted at the
144
150
  * resolved base path.
145
151
  *
146
152
  * When `distDir` is omitted (and the definition's client assets are unset) the
147
153
  * server runs in **bridge mode**: only `__connection.json` and the WS
148
154
  * endpoint are mounted, with no SPA mount. The SPA is expected to be
149
- * hosted elsewhere (e.g. by a parent Vite/Nuxt dev server) — see
155
+ * hosted elsewhere (e.g. by a parent Vite/Nuxt dev server); see
150
156
  * `devframeViteBridge` from `@devframes/vite`.
151
157
  *
152
158
  * Returns the underlying {@link StartedServer} handle so callers can
@@ -194,12 +200,25 @@ async function createDevServer(def, options = {}) {
194
200
  ws: options.ws,
195
201
  allowedOrigins: options.allowedOrigins,
196
202
  sse: options.sse,
203
+ /**
204
+ * The `--no-auth` flag forces the gate off regardless of the `auth`
205
+ * option / definition default (which the instance resolves itself).
206
+ */
197
207
  auth: flags.auth === false ? false : options.auth,
198
208
  mcp: options.mcp,
199
209
  flags,
200
210
  onPeerConnect: options.onPeerConnect,
201
211
  onPeerDisconnect: options.onPeerDisconnect,
212
+ /**
213
+ * Publish in the global registry so discovery tooling (`devframe connect`)
214
+ * finds the server without port guessing. The instance owns the record's
215
+ * lifecycle, written once its (pinned) origin resolves, removed on close.
216
+ */
202
217
  register: true,
218
+ /**
219
+ * This server is devframe's own, and nothing else handles its upgrades, so
220
+ * off-route upgrade attempts are rejected promptly.
221
+ */
203
222
  destroyUnmatchedUpgrades: true
204
223
  });
205
224
  try {
@@ -1,5 +1,10 @@
1
- import { i as defineDiagnostics } from "./nostics-CzECRXpE.mjs";
1
+ import { i as defineDiagnostics } from "./nostics-D0PvLZsn.mjs";
2
2
  //#region src/node/diagnostics.ts
3
+ /**
4
+ * DF00xx codes are allocated across packages (e.g. @devframes/json-render
5
+ * owns DF0037–DF0041), so this file alone doesn't show the next free
6
+ * number; check `docs/content/6.errors/` for the full allocation before adding one.
7
+ */
3
8
  const diagnostics = defineDiagnostics({
4
9
  docsBase: "https://devfra.me/errors",
5
10
  codes: {
@@ -9,7 +14,7 @@ const diagnostics = defineDiagnostics({
9
14
  DF0012: { why: (p) => `Failed to parse storage file: ${p.filepath}, falling back to defaults.` },
10
15
  DF0013: { why: (p) => `Shared state of "${p.key}" is not found, please provide an initial value for the first time` },
11
16
  DF0014: {
12
- why: (p) => `RPC function "${p.name}" has an invalid \`agent\` field — \`description\` must be a non-empty string.`,
17
+ why: (p) => `RPC function "${p.name}" has an invalid \`agent\` field: \`description\` must be a non-empty string.`,
13
18
  fix: "Provide a short description (~1–3 sentences) explaining what the tool does and when agents should invoke it."
14
19
  },
15
20
  DF0015: {
@@ -23,12 +28,12 @@ const diagnostics = defineDiagnostics({
23
28
  fix: "The consumer is too slow for the producer. Raise `highWaterMark` on the subscription, slow the producer, or batch chunks."
24
29
  },
25
30
  DF0030: {
26
- why: (p) => `Stream "${p.channel}#${p.id}" is unknown — no producer has called \`channel.start({ id: "${p.id}" })\`.`,
31
+ why: (p) => `Stream "${p.channel}#${p.id}" is unknown; no producer has called \`channel.start({ id: "${p.id}" })\`.`,
27
32
  fix: "Ensure the server-side producer is running before clients subscribe, or check for typos in the stream id."
28
33
  },
29
34
  DF0031: {
30
35
  why: (p) => `Cannot write to closed stream "${p.channel}#${p.id}".`,
31
- fix: "Track the producer lifecycle — guard writes with the `stream.signal.aborted` flag."
36
+ fix: "Track the producer lifecycle by guarding writes with the `stream.signal.aborted` flag."
32
37
  },
33
38
  DF0032: {
34
39
  why: (p) => `Streaming channel "${p.channel}" is already registered.`,
@@ -48,14 +53,14 @@ const diagnostics = defineDiagnostics({
48
53
  },
49
54
  DF0036: {
50
55
  why: (p) => `RPC call to "${p.name}" was rejected: the caller is not authorized.`,
51
- fix: "Complete the auth handshake (or connect with a static/pre-shared token) before calling a trusted method. Untrusted callers may only call `anonymous:`-prefixed methods — see `isAnonymousRpcMethod`."
56
+ fix: "Complete the auth handshake (or connect with a static/pre-shared token) before calling a trusted method. Untrusted callers may only call `anonymous:`-prefixed methods; see `isAnonymousRpcMethod`."
52
57
  },
53
58
  DF0037: {
54
59
  why: (p) => `A service is already provided under "${p.id}".`,
55
60
  fix: "Service ids are unique per context. Revoke the existing provider first (the `provide()` call returns a revoke function), or namespace the id with your plugin id to avoid collisions."
56
61
  },
57
62
  DF0042: {
58
- why: (p) => `"${p.id}" declares \`capabilities.build: false\` — its static export is not meaningful (writes are excluded and any live-served data won't be there).`,
63
+ why: (p) => `"${p.id}" declares \`capabilities.build: false\`, so its static export is not meaningful (writes are excluded and any live-served data won't be there).`,
59
64
  fix: "Pass `{ force: true }` to `createBuild()` if the degraded export is still useful to you, or drop `capabilities.build: false` on the definition."
60
65
  },
61
66
  DF0045: {
@@ -80,7 +85,7 @@ const diagnostics = defineDiagnostics({
80
85
  },
81
86
  DF0050: {
82
87
  why: (p) => `No running devframe instance on port ${p.port}.`,
83
- fix: "Call devframe_connect_list-instances for the current instance list — the instance may have stopped or changed port."
88
+ fix: "Call devframe_connect_list-instances for the current instance list; the instance may have stopped or changed port."
84
89
  },
85
90
  DF0051: {
86
91
  why: (p) => `The devframe instance on port ${p.port} has no MCP endpoint.`,
@@ -92,7 +97,7 @@ const diagnostics = defineDiagnostics({
92
97
  },
93
98
  DF0054: {
94
99
  why: (p) => `connectionMeta() was called before initDevframe("${p.id}") finished initializing.`,
95
- fix: "Await `instance.ready` (or any request through `instance.handler`) before reading `connectionMeta()` — the WebSocket binding it describes is only known once initialization completes."
100
+ fix: "Await `instance.ready` (or any request through `instance.handler`) before reading `connectionMeta()`; the WebSocket binding it describes is only known once initialization completes."
96
101
  },
97
102
  DF0055: {
98
103
  why: (p) => `This instance already owns its WebSocket transport (${p.tier}), so it cannot take over the host's upgrade events.`,
@@ -104,10 +109,10 @@ const diagnostics = defineDiagnostics({
104
109
  },
105
110
  DF0057: {
106
111
  why: () => "This instance disables its WebSocket transport (`ws: false`), so there is no socket to drive upgrades into.",
107
- fix: "Clients connect over the SSE endpoint instead — no upgrade wiring is needed. Remove `ws: false` if the instance should serve a WebSocket after all."
112
+ fix: "Clients connect over the SSE endpoint instead, so no upgrade wiring is needed. Remove `ws: false` if the instance should serve a WebSocket after all."
108
113
  },
109
114
  DF0058: {
110
- why: (p) => `"${p.id}" declares \`capabilities.dev: false\` — it does not support a live dev server (its value is a static export only).`,
115
+ why: (p) => `"${p.id}" declares \`capabilities.dev: false\`, so it does not support a live dev server (its value is a static export only).`,
111
116
  fix: "Pass `{ force: true }` to `createDevServer()` to run it anyway, or drop `capabilities.dev: false` on the definition."
112
117
  },
113
118
  DF0059: {
@@ -120,10 +125,10 @@ const diagnostics = defineDiagnostics({
120
125
  },
121
126
  DF0061: {
122
127
  why: (p) => `The locally installed "${p.package}@${p.installed}" is a different major version than the required "${p.required}".`,
123
- fix: "Align the installed assets package with the version its node package declares — they are published in lockstep."
128
+ fix: "Align the installed assets package with the version its node package declares; they are published in lockstep."
124
129
  },
125
130
  DF0062: {
126
- why: (p) => `The locally installed "${p.package}@${p.installed}" differs from the required "${p.required}" — serving the installed one.`,
131
+ why: (p) => `The locally installed "${p.package}@${p.installed}" differs from the required "${p.required}"; serving the installed one.`,
127
132
  fix: "Install the exact declared version to serve byte-identical assets."
128
133
  },
129
134
  DF0063: {
@@ -136,10 +141,10 @@ const diagnostics = defineDiagnostics({
136
141
  },
137
142
  DF0065: {
138
143
  why: (p) => `Invalid remote-assets ${p.field} "${p.value}".`,
139
- fix: "A remote-assets `package` must be a valid npm package name and `version` an exact semver version (e.g. `1.2.3`) — they are interpolated into CDN URLs and the cache path."
144
+ fix: "A remote-assets `package` must be a valid npm package name and `version` an exact semver version (e.g. `1.2.3`); they are interpolated into CDN URLs and the cache path."
140
145
  },
141
146
  DF0066: {
142
- why: (p) => `Service "${p.package}" is already installed — keeping the first installation and ignoring this one's options.`,
147
+ why: (p) => `Service "${p.package}" is already installed; keeping the first installation and ignoring this one's options.`,
143
148
  fix: "Option sets only merge before `ctx.services.ready()` fires. Install the service (or declare it in `DevframeDefinition.services`) before the barrier so its options join the merge."
144
149
  },
145
150
  DF0067: {
@@ -151,16 +156,24 @@ const diagnostics = defineDiagnostics({
151
156
  fix: "Align the installed service package with the range its declarer requires, or drop `required: true` to downgrade the mismatch to a warning."
152
157
  },
153
158
  DF0069: {
154
- why: (p) => `The installed service "${p.package}@${p.installed}" does not satisfy the declared range "${p.required}" — installing it anyway.`,
159
+ why: (p) => `The installed service "${p.package}@${p.installed}" does not satisfy the declared range "${p.required}"; installing it anyway.`,
155
160
  fix: "The advertised meta carries the real version, so clients can gate on it. Align the installed service package with the declared range to silence this warning."
156
161
  },
157
162
  DF0070: {
158
163
  why: (p) => `Invalid service "${p.package}": ${p.reason}`,
159
- fix: "A service package's default export must be a factory returning a `DevframeServiceDefinition` — an object with `package`, `version`, `scope`, and a `setup` function."
164
+ fix: "A service package's default export must be a factory returning a `DevframeServiceDefinition`, an object with `package`, `version`, `scope`, and a `setup` function."
160
165
  },
161
166
  DF0072: {
162
- why: (p) => `\`rpc.snapshot\` names "${p.method}", but no RPC function is registered under that id — nothing to bake into the static build.`,
167
+ why: (p) => `\`rpc.snapshot\` names "${p.method}", but no RPC function is registered under that id, so nothing to bake into the static build.`,
163
168
  fix: "Check the method id, and ensure the service/plugin that registers it is installed (e.g. declared in `services`) before the build collects the dump."
169
+ },
170
+ DF0075: {
171
+ why: (p) => `On ${p.runtime} the shared server's WebSocket upgrade needs crossws's Node adapter, which refuses to run off Node, and SSE is disabled, so this instance advertises no RPC transport at all.`,
172
+ fix: "Keep the SSE endpoint enabled (drop `sse: false`) so clients connect over it on Bun/Deno, or move the socket to a side-car (`ws: { sidecar: true }`) which binds the native WebSocket adapter on its own port."
173
+ },
174
+ DF0076: {
175
+ why: (p) => `\`attach\` / \`handleUpgrade\` drive a raw \`node:http\` upgrade into crossws's Node adapter, which refuses to run on ${p.runtime}.`,
176
+ fix: "On Bun/Deno, serve the advertised `__ws` route from `Bun.serve` / `Deno.serve` with `attachBunWsTransport` / `attachDenoWsTransport` (see the hub-deno-minimal example), or connect over the SSE endpoint instead."
164
177
  }
165
178
  }
166
179
  });
@@ -1,10 +1,10 @@
1
- import { i as defineDiagnostics } from "./nostics-CzECRXpE.mjs";
1
+ import { i as defineDiagnostics } from "./nostics-D0PvLZsn.mjs";
2
2
  //#region src/rpc/diagnostics.ts
3
3
  const diagnostics = defineDiagnostics({
4
4
  docsBase: "https://devfra.me/errors",
5
5
  codes: {
6
6
  DF0019: {
7
- why: (p) => `RPC function "${p.name}" has \`agent\` set but \`jsonSerializable\` is not \`true\` — MCP requires JSON-serializable data.`,
7
+ why: (p) => `RPC function "${p.name}" has \`agent\` set but \`jsonSerializable\` is not \`true\`; MCP requires JSON-serializable data.`,
8
8
  fix: "Set `jsonSerializable: true` if the payload is JSON-safe, or remove `agent` to keep it RPC-only."
9
9
  },
10
10
  DF0020: {