devframe 0.9.11 → 0.9.12

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.
@@ -3,7 +3,7 @@ import { t as resolveClientAssets } from "../client-assets-DNdhagGm.mjs";
3
3
  import { DEVFRAME_CONNECTION_META_FILENAME, DEVFRAME_RPC_DUMP_MANIFEST_FILENAME } from "../constants.mjs";
4
4
  import { n as collectStaticRpcDump, t as writeStaticRpcDump } from "../write-BuLCrn9h.mjs";
5
5
  import { t as diagnostics } from "../diagnostics-DZA6KQ_Z.mjs";
6
- import { t as createHostContext } from "../context-Bq27SDK-.mjs";
6
+ import { t as createHostContext } from "../context-DwGevWqT.mjs";
7
7
  import { t as resolveStaticAssetsSource } from "../remote-assets-DMFzLSoH.mjs";
8
8
  import { t as createH3DevframeHost } from "../host-h3-BROI6SQ4.mjs";
9
9
  import fs from "node:fs/promises";
@@ -1,7 +1,7 @@
1
1
  import { s as colors } from "../nostics-D0PvLZsn.mjs";
2
2
  import { i as resolveDevServerPort, s as importRuntimeModule } from "../_shared-U8D-xzrp.mjs";
3
3
  import { createBuild } from "./build.mjs";
4
- import { t as createDevServer } from "../dev-B32EVw5w.mjs";
4
+ import { t as createDevServer } from "../dev-CZ1fu4J1.mjs";
5
5
  import process from "node:process";
6
6
  import cac$1 from "cac";
7
7
  //#region src/adapters/flags.ts
@@ -1,7 +1,7 @@
1
1
  import { c as DevframeNodeRpcSessionMeta, l as DevframeRpcConnection, n as WsOriginRegistry } from "../ws-server-D2nv68Hg.mjs";
2
2
  import { F as DevframeDefinition, Tt as DevframeAuthHandler, U as DevframeSseOptions, W as DevframeWsOptions, q as McpSetting, s as DevframeNodeRpcSession } from "../context-BBWPLDHh.mjs";
3
3
  import { o as StaticAssetsSource } from "../remote-assets-eBvSKZMS.mjs";
4
- import { c as StartedServer } from "../instance-shell-BWh0JQuo.mjs";
4
+ import { c as StartedServer } from "../instance-shell-BUyYrKlu.mjs";
5
5
  import { c as resolveMcpConnectionMeta, o as resolveDevServerPort, t as ResolveDevServerPortOptions } from "../_shared-DEAMiPfH.mjs";
6
6
  import { H3 } from "h3";
7
7
  //#region src/adapters/dev.d.ts
@@ -1,3 +1,3 @@
1
1
  import { i as resolveDevServerPort, o as resolveMcpConnectionMeta } from "../_shared-U8D-xzrp.mjs";
2
- import { t as createDevServer } from "../dev-B32EVw5w.mjs";
2
+ import { t as createDevServer } from "../dev-CZ1fu4J1.mjs";
3
3
  export { createDevServer, resolveDevServerPort, resolveMcpConnectionMeta };
@@ -1,7 +1,7 @@
1
1
  import { c as DevframeNodeRpcSessionMeta, l as DevframeRpcConnection, n as WsOriginRegistry } from "../ws-server-D2nv68Hg.mjs";
2
2
  import { F as DevframeDefinition, St as DevframeStorageScope, Tt as DevframeAuthHandler, U as DevframeSseOptions, W as DevframeWsOptions, o as DevframeNodeContext, q as McpSetting, s as DevframeNodeRpcSession, t as ConnectionMeta } from "../context-BBWPLDHh.mjs";
3
3
  import { o as StaticAssetsSource } from "../remote-assets-eBvSKZMS.mjs";
4
- import { c as StartedServer, f as DevframeInstanceRecord } from "../instance-shell-BWh0JQuo.mjs";
4
+ import { c as StartedServer, f as DevframeInstanceRecord } from "../instance-shell-BUyYrKlu.mjs";
5
5
  import { Buffer } from "node:buffer";
6
6
  import { IncomingMessage, Server, ServerResponse } from "node:http";
7
7
  import { Duplex } from "node:stream";
@@ -1,2 +1,2 @@
1
- import { n as getInstanceInternals, r as initDevframe } from "../dev-B32EVw5w.mjs";
1
+ import { n as getInstanceInternals, r as initDevframe } from "../dev-CZ1fu4J1.mjs";
2
2
  export { getInstanceInternals, initDevframe };
@@ -3,7 +3,7 @@ import { t as DEVFRAME_EVENTS } from "../events-BV4Dj59a.mjs";
3
3
  import { t as isAllowedOrigin } from "../origin-DZit9g29.mjs";
4
4
  import { r as timingSafeEqual } from "../crypto-token-TVbGGFuj.mjs";
5
5
  import { t as diagnostics } from "../diagnostics-DZA6KQ_Z.mjs";
6
- import { t as createHostContext } from "../context-Bq27SDK-.mjs";
6
+ import { t as createHostContext } from "../context-DwGevWqT.mjs";
7
7
  import { t as toAgentToolName } from "../agent-tool-name-DGzeuWcO.mjs";
8
8
  import { join } from "pathe";
9
9
  import process from "node:process";
@@ -126,11 +126,8 @@ function returnToJsonSchema(schema) {
126
126
  */
127
127
  function argsToJsonSchema(args) {
128
128
  if (!args || args.length === 0) return {
129
- schema: {
130
- type: "object",
131
- properties: {}
132
- },
133
- unwrapped: false
129
+ type: "object",
130
+ properties: {}
134
131
  };
135
132
  const properties = {};
136
133
  const required = [];
@@ -140,13 +137,10 @@ function argsToJsonSchema(args) {
140
137
  required.push(key);
141
138
  }
142
139
  return {
143
- schema: {
144
- type: "object",
145
- properties,
146
- required,
147
- additionalProperties: false
148
- },
149
- unwrapped: false
140
+ type: "object",
141
+ properties,
142
+ required,
143
+ additionalProperties: false
150
144
  };
151
145
  }
152
146
  //#endregion
@@ -443,7 +437,7 @@ function projectTool(name, tool, ctx) {
443
437
  };
444
438
  }
445
439
  function computeInputSchema(tool, ctx) {
446
- if (tool.kind === "tool") return argsToJsonSchema(tool.args).schema;
440
+ if (tool.kind === "tool") return argsToJsonSchema(tool.args);
447
441
  if (tool.kind !== "rpc" || !tool.rpcName) return {
448
442
  type: "object",
449
443
  properties: {}
@@ -454,7 +448,7 @@ function computeInputSchema(tool, ctx) {
454
448
  properties: {}
455
449
  };
456
450
  const args = def.args;
457
- return argsToJsonSchema(args).schema;
451
+ return argsToJsonSchema(args);
458
452
  }
459
453
  function computeOutputSchema(tool, ctx) {
460
454
  if (tool.kind !== "rpc" || !tool.rpcName) return void 0;
@@ -968,20 +968,6 @@ function createRpcClient(functions, options) {
968
968
  });
969
969
  }
970
970
  //#endregion
971
- //#region src/utils/promise.ts
972
- function promiseWithResolver() {
973
- let resolve;
974
- let reject;
975
- return {
976
- promise: new Promise((_resolve, _reject) => {
977
- resolve = _resolve;
978
- reject = _reject;
979
- }),
980
- resolve,
981
- reject
982
- };
983
- }
984
- //#endregion
985
971
  //#region src/client/rpc-live.ts
986
972
  /**
987
973
  * The transport-independent half of a live RPC client mode (connection
@@ -993,7 +979,7 @@ function createLiveRpcClientMode(options) {
993
979
  let isTrusted = false;
994
980
  let status = "connecting";
995
981
  let connectionError = null;
996
- const trustedPromise = promiseWithResolver();
982
+ const trustedPromise = Promise.withResolvers();
997
983
  function setStatus(next, error = null) {
998
984
  if (error) connectionError = error;
999
985
  else if (next === "connected") connectionError = null;
@@ -1604,14 +1590,14 @@ function qualifyName(namespace, name) {
1604
1590
  return isQualifiedName(name) ? name : `${namespace}:${name}`;
1605
1591
  }
1606
1592
  //#endregion
1607
- //#region src/client/settings.ts
1608
- function createClientSettingsStore(rpc, namespace, scope) {
1609
- const stateKey = `devframe:settings:${scope}:${namespace}`;
1610
- let statePromise;
1611
- function store() {
1612
- if (!statePromise) statePromise = rpc.sharedState.get(stateKey, { initialValue: {} });
1613
- return statePromise;
1614
- }
1593
+ //#region src/settings-store.ts
1594
+ /**
1595
+ * The key-value store surface over a lazily-resolved shared state, shared by
1596
+ * the node-side (file-backed) and client-side (RPC-mirrored) settings stores.
1597
+ * Each side supplies its own `store()` resolver; the read/write/subscribe
1598
+ * behavior on top of it is identical.
1599
+ */
1600
+ function createSettingsStore(store) {
1615
1601
  return {
1616
1602
  async get(key) {
1617
1603
  return (await store()).value()[key];
@@ -1634,6 +1620,17 @@ function createClientSettingsStore(rpc, namespace, scope) {
1634
1620
  }
1635
1621
  };
1636
1622
  }
1623
+ //#endregion
1624
+ //#region src/client/settings.ts
1625
+ function createClientSettingsStore(rpc, namespace, scope) {
1626
+ const stateKey = `devframe:settings:${scope}:${namespace}`;
1627
+ let statePromise;
1628
+ function store() {
1629
+ if (!statePromise) statePromise = rpc.sharedState.get(stateKey, { initialValue: {} });
1630
+ return statePromise;
1631
+ }
1632
+ return createSettingsStore(store);
1633
+ }
1637
1634
  /**
1638
1635
  * Build the client-side `settings` surface for a scope namespace. Mirrors
1639
1636
  * the node-side stores over the shared-state sync protocol.
@@ -1134,25 +1134,14 @@ function qualifyName(namespace, name) {
1134
1134
  return isQualifiedName(name) ? name : `${namespace}:${name}`;
1135
1135
  }
1136
1136
  //#endregion
1137
- //#region src/node/settings.ts
1138
- const STORAGE_SCOPE = {
1139
- global: "global",
1140
- project: "project"
1141
- };
1142
- function createNodeSettingsStore(context, namespace, scope) {
1143
- const stateKey = `devframe:settings:${scope}:${namespace}`;
1144
- let statePromise;
1145
- function store() {
1146
- if (!statePromise) {
1147
- const dir = context.host.getStorageDir(STORAGE_SCOPE[scope]);
1148
- const filepath = join(dir, "settings", `${namespace}.json`);
1149
- statePromise = context.rpc.sharedState.get(stateKey, { sharedState: createStorage({
1150
- filepath,
1151
- initialValue: {}
1152
- }) });
1153
- }
1154
- return statePromise;
1155
- }
1137
+ //#region src/settings-store.ts
1138
+ /**
1139
+ * The key-value store surface over a lazily-resolved shared state, shared by
1140
+ * the node-side (file-backed) and client-side (RPC-mirrored) settings stores.
1141
+ * Each side supplies its own `store()` resolver; the read/write/subscribe
1142
+ * behavior on top of it is identical.
1143
+ */
1144
+ function createSettingsStore(store) {
1156
1145
  return {
1157
1146
  async get(key) {
1158
1147
  return (await store()).value()[key];
@@ -1175,6 +1164,28 @@ function createNodeSettingsStore(context, namespace, scope) {
1175
1164
  }
1176
1165
  };
1177
1166
  }
1167
+ //#endregion
1168
+ //#region src/node/settings.ts
1169
+ const STORAGE_SCOPE = {
1170
+ global: "global",
1171
+ project: "project"
1172
+ };
1173
+ function createNodeSettingsStore(context, namespace, scope) {
1174
+ const stateKey = `devframe:settings:${scope}:${namespace}`;
1175
+ let statePromise;
1176
+ function store() {
1177
+ if (!statePromise) {
1178
+ const dir = context.host.getStorageDir(STORAGE_SCOPE[scope]);
1179
+ const filepath = join(dir, "settings", `${namespace}.json`);
1180
+ statePromise = context.rpc.sharedState.get(stateKey, { sharedState: createStorage({
1181
+ filepath,
1182
+ initialValue: {}
1183
+ }) });
1184
+ }
1185
+ return statePromise;
1186
+ }
1187
+ return createSettingsStore(store);
1188
+ }
1178
1189
  /**
1179
1190
  * Build the node-side `settings` surface for a scope namespace. `project`
1180
1191
  * persists under the host's `workspace` storage dir, `global` under its
@@ -1,11 +1,11 @@
1
1
  import { t as resolveClientAssets } from "./client-assets-DNdhagGm.mjs";
2
2
  import { DEVFRAME_CONNECTION_META_FILENAME } from "./constants.mjs";
3
3
  import { t as diagnostics } from "./diagnostics-DZA6KQ_Z.mjs";
4
- import { t as createHostContext } from "./context-Bq27SDK-.mjs";
4
+ import { t as createHostContext } from "./context-DwGevWqT.mjs";
5
5
  import { t as resolveStaticAssetsSource } from "./remote-assets-DMFzLSoH.mjs";
6
6
  import { a as resolveMcpConfig, i as resolveDevServerPort, n as normalizeBasePath, r as resolveBasePath, s as importRuntimeModule, t as loadAutoMcpAdapter } from "./_shared-U8D-xzrp.mjs";
7
7
  import { t as createH3DevframeHost } from "./host-h3-BROI6SQ4.mjs";
8
- import { i as normalizeHttpServerUrl, n as resolveInstanceRegister, t as createInstanceShell } from "./instance-shell-DvYCwrDy.mjs";
8
+ import { i as normalizeHttpServerUrl, n as resolveInstanceRegister, t as createInstanceShell } from "./instance-shell-xuu6jg51.mjs";
9
9
  import { open } from "./utils/open.mjs";
10
10
  import { mountStaticHandler } from "./utils/serve-static.mjs";
11
11
  import { resolve } from "pathe";
@@ -150,8 +150,15 @@ interface CreateInstanceShellOptions<TContext extends DevframeNodeContext> {
150
150
  app?: H3;
151
151
  /** Public origin, or a getter. Derived from the first request when omitted. */
152
152
  origin?: string | (() => string);
153
- /** Resolved auth intent: `undefined`/`true` gates, `false` opts out, a handler installs a scheme. */
154
- auth?: boolean | DevframeAuthHandler;
153
+ /**
154
+ * Resolved auth intent: `undefined`/`true` gates with {@link createInteractiveAuth}'s
155
+ * defaults, `false` opts out, a handler installs a custom scheme outright.
156
+ * A function gates too, but builds the handler itself from the now-ready
157
+ * `ctx` - the seam a wrapping host (e.g. `@devframes/hub`'s UI slot) uses to
158
+ * hand `createInteractiveAuth` a branded `banner` while still leaving `auth`
159
+ * itself unset for the caller.
160
+ */
161
+ auth?: boolean | DevframeAuthHandler | ((ctx: TContext) => DevframeAuthHandler);
155
162
  /** Host `node:http` server to share the WS upgrade with. */
156
163
  server?: Server;
157
164
  /** Explicit WebSocket control; see {@link DevframeWsOptions}. `false` disables the socket (SSE-only). */
@@ -366,11 +366,16 @@ function createInstanceShell(options) {
366
366
  };
367
367
  /**
368
368
  * Auth resolution: gate by default, `false` opts out, a handler object
369
- * installs a custom scheme. The `external` tier has no local transport to
370
- * gate (the server behind `ws.url` owns auth) so it resolves to nothing.
369
+ * installs a custom scheme, a function builds one from `ctx`. The `external`
370
+ * tier has no local transport to gate (the server behind `ws.url` owns
371
+ * auth) so it resolves to nothing.
371
372
  */
372
373
  function resolveAuth() {
373
374
  if (options.auth === false) return false;
375
+ if (typeof options.auth === "function") {
376
+ authHandler = options.auth(ctx);
377
+ return authHandler;
378
+ }
374
379
  if (typeof options.auth === "object") {
375
380
  authHandler = options.auth;
376
381
  return options.auth;
@@ -1,7 +1,7 @@
1
1
  import { v as RpcFunctionDefinitionAny } from "../types-Dq4i156u.mjs";
2
2
  import { At as AgentHandle, Bt as DevframeAgentHostEvents, F as DevframeDefinition, Ft as AgentTool, It as AgentToolInput, Lt as AgentToolProvider, Mt as AgentResource, Nt as AgentResourceContent, Pt as AgentResourceInput, Rt as AgentToolProviderHandle, Vt as EventEmitter, jt as AgentManifest, o as DevframeNodeContext, xt as DevframeHost, zt as DevframeAgentHost$1 } from "../context-BBWPLDHh.mjs";
3
3
  import { a as RemoteAssetsStore, o as StaticAssetsSource } from "../remote-assets-eBvSKZMS.mjs";
4
- import { a as InstanceShellInit, c as StartedServer, d as samePath, f as DevframeInstanceRecord, h as registerDevframeInstance, i as InstanceShellApi, l as createInstanceShell, m as listLiveDevframeInstances, n as InstanceRegisterConfig, o as InstanceShellInternals, p as DevframeInstanceRegistration, r as InstanceShell, s as InstanceWsTier, t as CreateInstanceShellOptions, u as resolveInstanceRegister } from "../instance-shell-BWh0JQuo.mjs";
4
+ import { a as InstanceShellInit, c as StartedServer, d as samePath, f as DevframeInstanceRecord, h as registerDevframeInstance, i as InstanceShellApi, l as createInstanceShell, m as listLiveDevframeInstances, n as InstanceRegisterConfig, o as InstanceShellInternals, p as DevframeInstanceRegistration, r as InstanceShell, s as InstanceWsTier, t as CreateInstanceShellOptions, u as resolveInstanceRegister } from "../instance-shell-BUyYrKlu.mjs";
5
5
  import { a as resolveBasePath, i as normalizeBasePath, n as ResolvedMcpConfig, r as loadAutoMcpAdapter, s as resolveMcpConfig } from "../_shared-DEAMiPfH.mjs";
6
6
  import { n as CreateContextRpcServerOptions, r as createContextRpcServer, t as ContextRpcServer } from "../rpc-core-DiCr5etS.mjs";
7
7
  //#region src/client-assets.d.ts
@@ -5,6 +5,6 @@ import { n as coerceAgentPositionalArgs, t as DevframeAgentHost } from "../host-
5
5
  import { a as resolveMcpConfig, n as normalizeBasePath, r as resolveBasePath, s as importRuntimeModule, t as loadAutoMcpAdapter } from "../_shared-U8D-xzrp.mjs";
6
6
  import { t as createH3DevframeHost } from "../host-h3-BROI6SQ4.mjs";
7
7
  import { i as registerDevframeInstance, n as listLiveDevframeInstances } from "../instance-registry-DCq762YJ.mjs";
8
- import { i as normalizeHttpServerUrl, n as resolveInstanceRegister, r as samePath, t as createInstanceShell } from "../instance-shell-DvYCwrDy.mjs";
8
+ import { i as normalizeHttpServerUrl, n as resolveInstanceRegister, r as samePath, t as createInstanceShell } from "../instance-shell-xuu6jg51.mjs";
9
9
  import { t as createContextRpcServer } from "../rpc-core-CvkU23bQ.mjs";
10
10
  export { DevframeAgentHost, coerceAgentPositionalArgs, createContextRpcServer, createH3DevframeHost, createInstanceShell, createRpcWireCodec, diagnostics, importRuntimeModule, listLiveDevframeInstances, loadAutoMcpAdapter, normalizeBasePath, normalizeHttpServerUrl, peekRpcWireFrame, registerDevframeInstance, resolveBasePath, resolveClientAssets, resolveInstanceRegister, resolveMcpConfig, samePath };
@@ -1,3 +1,3 @@
1
- import { t as createHostContext } from "../context-Bq27SDK-.mjs";
1
+ import { t as createHostContext } from "../context-DwGevWqT.mjs";
2
2
  import { t as createStorage } from "../storage-B70AXK64.mjs";
3
3
  export { createHostContext, createStorage };
@@ -1,5 +1,6 @@
1
1
  import { Tt as DevframeAuthHandler, o as DevframeNodeContext, s as DevframeNodeRpcSession } from "../context-BBWPLDHh.mjs";
2
2
  import "../index-1ezCLThh.mjs";
3
+ import { ColorFn } from "../utils/colors.mjs";
3
4
  //#region src/recipes/interactive-auth.d.ts
4
5
  interface CreateInteractiveAuthOptions {
5
6
  /**
@@ -12,14 +13,12 @@ interface CreateInteractiveAuthOptions {
12
13
  /**
13
14
  * Print the current code + magic-link URL. Devframe stays headless, so
14
15
  * there is no default banner printed automatically; call
15
- * `auth.printBanner()` yourself once the server is listening. Override
16
- * this to customize the format; defaults to a small boxed message on
17
- * stdout.
16
+ * `auth.printBanner()` yourself once the server is listening. Defaults to
17
+ * {@link createAuthBanner}'s output; pass its result here directly to
18
+ * rebrand the box (title / colors), or your own function to replace the
19
+ * format outright.
18
20
  */
19
- banner?: (info: {
20
- code: string;
21
- url: string;
22
- }) => void;
21
+ banner?: AuthBannerFunction;
23
22
  /**
24
23
  * Called once a code exchange succeeds, so a host rendering its own
25
24
  * banner can retract it. Fires after the rotated code is printed, so
@@ -37,6 +36,33 @@ interface CreateInteractiveAuthOptions {
37
36
  */
38
37
  serverUrl?: () => string;
39
38
  }
39
+ /** Signature of `options.banner`: render the current auth code + magic-link URL. */
40
+ type AuthBannerFunction = (info: {
41
+ code: string;
42
+ url: string;
43
+ }) => void;
44
+ /** Palette for {@link createAuthBanner}'s box - one color per part, so a host can rebrand a subset. */
45
+ interface CreateAuthBannerColorsOptions {
46
+ border: ColorFn;
47
+ title: ColorFn;
48
+ label: ColorFn;
49
+ code: ColorFn;
50
+ url: ColorFn;
51
+ }
52
+ interface CreateAuthBannerOptions {
53
+ /** Box title. Defaults to `'Devframe'` - set to your product name for branding. */
54
+ title?: string;
55
+ /** Palette overrides; unset colors fall back to dim/bold/cyan defaults. */
56
+ colors?: Partial<CreateAuthBannerColorsOptions>;
57
+ }
58
+ /**
59
+ * Build a {@link AuthBannerFunction} that renders the auth code + magic-link
60
+ * URL as a small bordered box, its two rows label-aligned. `createInteractiveAuth`
61
+ * falls back to `createAuthBanner()` when no `banner` is given; call this
62
+ * yourself to rebrand the box (`title` / `colors`) and pass the result as
63
+ * `options.banner`.
64
+ */
65
+ declare function createAuthBanner(options?: CreateAuthBannerOptions): AuthBannerFunction;
40
66
  /**
41
67
  * Package the interactive OTP auth protocol devframe's primitives
42
68
  * (`exchangeTempAuthCode`, `verifyAuthToken`, `revokeAuthToken`,
@@ -60,4 +86,4 @@ interface CreateInteractiveAuthOptions {
60
86
  */
61
87
  declare function createInteractiveAuth(context: DevframeNodeContext, options?: CreateInteractiveAuthOptions): DevframeAuthHandler;
62
88
  //#endregion
63
- export { CreateInteractiveAuthOptions, createInteractiveAuth };
89
+ export { AuthBannerFunction, CreateAuthBannerColorsOptions, CreateAuthBannerOptions, CreateInteractiveAuthOptions, createAuthBanner, createInteractiveAuth };
@@ -5,8 +5,50 @@ import { a as verifyAuthToken, n as exchangeTempAuthCode, r as getTempAuthCode,
5
5
  import { t as getInternalContext } from "../context-jXO5vzjH.mjs";
6
6
  import { t as s } from "../simple-schema-Bi40l-P6.mjs";
7
7
  //#region src/recipes/interactive-auth.ts
8
- function defaultBanner(info) {
9
- console.log(`\n ${colors.dim("devframe auth code")} ${colors.bold(info.code)}\n ${colors.dim("or open")} ${colors.cyan(info.url)}\n`);
8
+ /**
9
+ * Build a {@link AuthBannerFunction} that renders the auth code + magic-link
10
+ * URL as a small bordered box, its two rows label-aligned. `createInteractiveAuth`
11
+ * falls back to `createAuthBanner()` when no `banner` is given; call this
12
+ * yourself to rebrand the box (`title` / `colors`) and pass the result as
13
+ * `options.banner`.
14
+ */
15
+ function createAuthBanner(options = {}) {
16
+ const title = options.title ?? "Devframe";
17
+ const palette = {
18
+ border: colors.dim,
19
+ title: colors.bold,
20
+ label: colors.dim,
21
+ code: colors.bold,
22
+ url: colors.cyan,
23
+ ...options.colors
24
+ };
25
+ return (info) => {
26
+ const rows = [[
27
+ "auth code",
28
+ info.code,
29
+ palette.code
30
+ ], [
31
+ "or open",
32
+ info.url,
33
+ palette.url
34
+ ]];
35
+ const labelWidth = Math.max(...rows.map(([label]) => label.length));
36
+ const contentWidth = Math.max(...rows.map(([, value]) => labelWidth + 2 + value.length));
37
+ const titleBarLength = title.length + 3;
38
+ const lineWidth = Math.max(contentWidth, titleBarLength - 2);
39
+ const top = [
40
+ palette.border(`╭─`),
41
+ palette.title(title),
42
+ palette.border(`${"─".repeat(Math.max(lineWidth + 2 - titleBarLength, 0))}╮`)
43
+ ].join(" ");
44
+ const bottom = `╰${"─".repeat(lineWidth + 2)}╯`;
45
+ const body = rows.map(([label, value, color]) => {
46
+ const plain = `${label.padEnd(labelWidth)} ${value}`;
47
+ const pad = " ".repeat(lineWidth - plain.length);
48
+ return `${palette.border("│")} ${palette.label(label.padEnd(labelWidth))} ${color(value)}${pad} ${palette.border("│")}`;
49
+ });
50
+ console.log(`\n${palette.border(top)}\n${body.join("\n")}\n${palette.border(bottom)}\n`);
51
+ };
10
52
  }
11
53
  /**
12
54
  * Package the interactive OTP auth protocol devframe's primitives
@@ -39,13 +81,14 @@ function createInteractiveAuth(context, options = {}) {
39
81
  function resolveServerUrl() {
40
82
  return options.serverUrl?.() ?? context.host.resolveOrigin();
41
83
  }
84
+ const banner = options.banner ?? createAuthBanner();
42
85
  let bannerPrintedForCode;
43
86
  function printBanner() {
44
87
  const code = getTempAuthCode();
45
88
  if (code === bannerPrintedForCode) return;
46
89
  bannerPrintedForCode = code;
47
90
  const url = buildOtpAuthUrl(resolveServerUrl(), code);
48
- (options.banner ?? defaultBanner)({
91
+ banner({
49
92
  code,
50
93
  url
51
94
  });
@@ -146,4 +189,4 @@ function createInteractiveAuth(context, options = {}) {
146
189
  };
147
190
  }
148
191
  //#endregion
149
- export { createInteractiveAuth };
192
+ export { createAuthBanner, createInteractiveAuth };
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "devframe",
3
3
  "type": "module",
4
- "version": "0.9.11",
4
+ "version": "0.9.12",
5
5
  "description": "Framework for building one portable devtool integration that runs in any viewer.",
6
6
  "author": "Anthony Fu <anthonyfu117@hotmail.com>",
7
7
  "license": "MIT",