experimental-ash 0.8.0 → 0.8.2

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.
@@ -1,4 +1,4 @@
1
- import{E as e,S as t,T as n,_ as r,a as i,c as a,d as o,g as s,h as c,l,m as u,p as ee,s as te,u as d,x as f,y as p}from"./paths-B-aiDznc.js";import{t as m}from"./authored-module-loader-Pt_g8xX2.js";import{t as h}from"./errors-DsO9xmQL.js";import{i as g,t as _}from"./package-DmsQgn4v.js";import{join as v,posix as y}from"node:path";import{mkdir as ne,readFile as re,readdir as ie,realpath as b,writeFile as x}from"node:fs/promises";import{createHash as S}from"node:crypto";import{existsSync as C}from"node:fs";function w(e){return e.dev?{appRoot:e.appRoot,dev:e.dev,moduleMapLoaderPath:g(`src/internal/authored-module-map-loader.ts`)}:{appRoot:e.appRoot,dev:e.dev}}const T=`#ash-channel/`;function E(e){let t=e.compileResult.manifest.channels,n=new Set,r=[],i=new Set,o=a();for(let e of t){if(e.kind===`disabled`){if(!o.has(e.name))throw Error(`agent/channels/${e.name}.ts exports disableRoute() but "${e.name}" is not a framework channel. Rename the file to one of: ${[...o].sort().join(`, `)}.`);i.add(e.name);continue}n.add(e.name),r.push({method:e.method,route:e.urlPath})}let s=l().filter(e=>!n.has(e.name)&&!i.has(e.name)).map(e=>({method:e.method,route:e.urlPath})),c=new Set,u=[];for(let e of[...s,...r]){let t=k(e);c.has(t)||(c.add(t),u.push(e))}return u}function D(e,t){for(let n of t.registrations)A(e,{artifactsConfig:t.artifactsConfig,method:n.method,route:n.route})}function O(e,t){return N(t.previous,t.next)?!1:(j(e),D(e,{artifactsConfig:t.artifactsConfig,registrations:t.next}),e.routing.sync(),!0)}function k(e){return`${e.method.toUpperCase()} ${e.route}`}function A(e,t){let r=k(t),i=`${T}${r}`,a=n(g(`src/internal/nitro/routes/channel-dispatch.ts`));e.options.handlers.push({handler:i,method:t.method,route:t.route}),e.options.virtual[i]=[`import { dispatchChannelRequest } from ${a};`,`const config = ${JSON.stringify(t.artifactsConfig)};`,`export default (event) => dispatchChannelRequest(event, ${JSON.stringify(r)}, config);`].join(`
1
+ import{E as e,S as t,T as n,_ as r,a as i,c as a,d as o,g as s,h as c,l,m as u,p as ee,s as te,u as d,x as f,y as p}from"./paths-B-Onq-sx.js";import{t as m}from"./authored-module-loader-Pt_g8xX2.js";import{t as h}from"./errors-DsO9xmQL.js";import{i as g,t as _}from"./package-DmsQgn4v.js";import{join as v,posix as y}from"node:path";import{mkdir as ne,readFile as re,readdir as ie,realpath as b,writeFile as x}from"node:fs/promises";import{createHash as S}from"node:crypto";import{existsSync as C}from"node:fs";function w(e){return e.dev?{appRoot:e.appRoot,dev:e.dev,moduleMapLoaderPath:g(`src/internal/authored-module-map-loader.ts`)}:{appRoot:e.appRoot,dev:e.dev}}const T=`#ash-channel/`;function E(e){let t=e.compileResult.manifest.channels,n=new Set,r=[],i=new Set,o=a();for(let e of t){if(e.kind===`disabled`){if(!o.has(e.name))throw Error(`agent/channels/${e.name}.ts exports disableRoute() but "${e.name}" is not a framework channel. Rename the file to one of: ${[...o].sort().join(`, `)}.`);i.add(e.name);continue}n.add(e.name),r.push({method:e.method,route:e.urlPath})}let s=l().filter(e=>!n.has(e.name)&&!i.has(e.name)).map(e=>({method:e.method,route:e.urlPath})),c=new Set,u=[];for(let e of[...s,...r]){let t=k(e);c.has(t)||(c.add(t),u.push(e))}return u}function D(e,t){for(let n of t.registrations)A(e,{artifactsConfig:t.artifactsConfig,method:n.method,route:n.route})}function O(e,t){return N(t.previous,t.next)?!1:(j(e),D(e,{artifactsConfig:t.artifactsConfig,registrations:t.next}),e.routing.sync(),!0)}function k(e){return`${e.method.toUpperCase()} ${e.route}`}function A(e,t){let r=k(t),i=`${T}${r}`,a=n(g(`src/internal/nitro/routes/channel-dispatch.ts`));e.options.handlers.push({handler:i,method:t.method,route:t.route}),e.options.virtual[i]=[`import { dispatchChannelRequest } from ${a};`,`const config = ${JSON.stringify(t.artifactsConfig)};`,`export default (event) => dispatchChannelRequest(event, ${JSON.stringify(r)}, config);`].join(`
2
2
  `)}function j(e){for(let t=e.options.handlers.length-1;t>=0;--t){let n=e.options.handlers[t];n!==void 0&&M(n)&&e.options.handlers.splice(t,1)}for(let t of Object.keys(e.options.virtual))t.startsWith(T)&&delete e.options.virtual[t]}function M(e){return e.handler.startsWith(T)}function N(e,t){if(e.length!==t.length)return!1;for(let n=0;n<e.length;n+=1){let r=e[n],i=t[n];if(r===void 0||i===void 0||r.method!==i.method||r.route!==i.route)return!1}return!0}const P=`ash.schedule.`;var F=class extends Error{scheduleId;sourceId;taskName;constructor(e,t={}){super(e),this.name=`ScheduleRegistrationError`,t.scheduleId!==void 0&&(this.scheduleId=t.scheduleId),t.sourceId!==void 0&&(this.sourceId=t.sourceId),t.taskName!==void 0&&(this.taskName=t.taskName)}};function I(e){let t=e.map(e=>({cron:e.cron,description:`Run Ash schedule "${e.name}" from "${e.logicalPath}".`,logicalPath:e.logicalPath,scheduleId:e.name,sourceId:e.sourceId,taskName:R(e.sourceId)})).sort((e,t)=>e.sourceId.localeCompare(t.sourceId));return L(t),t}function L(e){let t=new Map;for(let n of e){let e=t.get(n.scheduleId);if(e===void 0){t.set(n.scheduleId,n);continue}throw new F(`Duplicate authored schedule id "${n.scheduleId}" found in "${e.logicalPath}" and "${n.logicalPath}".`,{scheduleId:n.scheduleId,sourceId:n.sourceId,taskName:n.taskName})}}function R(e){return`${P}${Buffer.from(e,`utf8`).toString(`base64url`)}`}const z=`#ash-schedule-task/`;function B(e,t){if(t.registrations.length!==0){e.options.experimental.tasks=!0;for(let n of t.registrations)U(e,{artifactsConfig:t.artifactsConfig,dispatchModulePath:t.dispatchModulePath,registration:n})}}function V(e,t){let n=!G(t.previous,t.next);return H(e),B(e,{artifactsConfig:t.artifactsConfig,dispatchModulePath:t.dispatchModulePath,registrations:t.next}),n}function H(e){for(let t of Object.keys(e.options.tasks))t.startsWith(`ash.schedule.`)&&delete e.options.tasks[t];for(let t of Object.keys(e.options.virtual))t.startsWith(z)&&delete e.options.virtual[t];for(let[t,n]of Object.entries(e.options.scheduledTasks)){let r=W(n).filter(e=>!e.startsWith(P));if(r.length===0){delete e.options.scheduledTasks[t];continue}if(r.length===1){let[n]=r;n!==void 0&&(e.options.scheduledTasks[t]=n);continue}e.options.scheduledTasks[t]=r}}function U(e,t){let r=`${z}${t.registration.taskName}`,i=n(t.dispatchModulePath);e.options.tasks[t.registration.taskName]={description:t.registration.description,handler:r},e.options.virtual[r]=[`import { dispatchScheduleTask } from ${i};`,`const config = ${JSON.stringify(t.artifactsConfig)};`,`export default {`,` meta: { description: ${JSON.stringify(t.registration.description)} },`,` async run(event) {`,` return { result: await dispatchScheduleTask(event.name, config) };`,` },`,`};`].join(`
3
3
  `),ae(e,t.registration.cron,t.registration.taskName)}function ae(e,t,n){let r=e.options.scheduledTasks[t];if(r===void 0){e.options.scheduledTasks[t]=n;return}if(typeof r==`string`){e.options.scheduledTasks[t]=[r,n];return}r.includes(n)||r.push(n)}function W(e){return typeof e==`string`?[e]:[...e]}function G(e,t){if(e.length!==t.length)return!1;for(let n=0;n<e.length;n+=1){let r=e[n],i=t[n];if(r===void 0||i===void 0||r.cron!==i.cron||r.description!==i.description||r.logicalPath!==i.logicalPath||r.scheduleId!==i.scheduleId||r.sourceId!==i.sourceId||r.taskName!==i.taskName)return!1}return!0}async function K(e){return[...e.manifest.schedules].map(e=>{let t={cron:e.cron,logicalPath:e.logicalPath,markdown:e.markdown,name:e.name,sourceId:e.sourceId,sourceKind:e.sourceKind};return e.channel===void 0?t:{...t,channel:e.channel}})}async function q(e){return await K({manifest:await o({compiledArtifactsSource:e.compiledArtifactsSource})})}async function J(e){let t=v(e.outDir,`compiled-artifacts-bootstrap.mjs`),n=v(e.outDir,`compiled-artifacts-instrumentation.mjs`),r=se(e.compileResult.manifest.agentRoot);await ne(e.outDir,{recursive:!0}),await x(t,await le({compileResult:e.compileResult,installModulePath:g(`src/runtime/loaders/bundled-artifacts.ts`),moduleMapPath:t,metadata:e.compileResult.metadata})),r!==void 0&&await x(n,ue({agentName:e.compileResult.manifest.config.name,instrumentationPath:r,registerConfigPath:g(`src/harness/instrumentation-config.ts`)}));let i={bootstrapPath:t};return r!==void 0&&(i.instrumentationPluginPath=n,i.instrumentationSourcePath=r),i}const oe=[`.ts`,`.mts`,`.js`,`.mjs`];function se(e){for(let t of oe){let n=v(e,`instrumentation${t}`);if(C(n))return n}}function ce(e){return e.replace(/^export const moduleMap = /m,`const moduleMap = `).replace(/\nexport default moduleMap;\n?$/,`
4
4
  `)}async function le(e){let r=ce(t({importSpecifierStyle:`absolute`,manifest:e.compileResult.manifest,moduleMapPath:e.moduleMapPath})).trim();return[`// Generated by Ash. Do not edit by hand.`,`import { installBundledCompiledArtifacts } from ${n(e.installModulePath)};`,``,r,``,`const metadata = ${JSON.stringify(e.metadata,null,2)};`,``,`const manifest = ${JSON.stringify(e.compileResult.manifest,null,2)};`,``,`export function installCompiledArtifactsBootstrap() {`,` installBundledCompiledArtifacts({`,` manifest,`,` metadata,`,` moduleMap,`,` });`,`}`,``,`installCompiledArtifactsBootstrap();`,``,`// Default export satisfies the Nitro plugin contract so this file`,`// can be used directly as a Nitro plugin without a separate wrapper.`,`export default function installCompiledArtifactsPlugin() {`,` // Already installed on import above.`,`}`,``,`export async function __ashInstallCompiledArtifactsStep() {`,` "use step";`,` return null;`,`}`,``].join(`
@@ -1,2 +1,2 @@
1
- import{D as e,b as t,t as n,v as r,y as i}from"../../chunks/paths-B-aiDznc.js";import{d as a,f as o,h as s}from"../../chunks/types-MZUhN0Zy.js";import{createCliTheme as c,renderCliBanner as l,renderCliSection as u}from"../ui/output.js";async function d(e){let t=await f(e);return{application:n(t?.project.appRoot??e),compiledState:t,messaging:{createSessionRoutePath:o,continueSessionRoutePattern:a,streamRoutePattern:s}}}async function f(n){try{return await i({startPath:n})}catch(n){if(n instanceof r)return n.result;if(n instanceof e||n instanceof t)return null;throw n}}function p(e,t){return`${e} ${t}${e===1?``:`s`}`}function m(e,t){return`${`${e} error${e===1?``:`s`}`}, ${`${t} warning${t===1?``:`s`}`}`}function h(e){switch(e){case`ready`:return`success`;case`failed`:return`danger`;default:return`warning`}}async function g(e,t){let n=await d(t),r=n.compiledState,i=n.application,a=c(),o=[{label:`App Root`,value:i.appRoot}],s=[{label:`Workflow Build`,value:i.workflowBuildDir},{label:`Output`,value:i.outputDir}],f=[];r===null?o.push({label:`Compile`,tone:`warning`,value:`unavailable`}):(o.push({label:`Agent Root`,value:r.project.agentRoot},{label:`Layout`,value:r.project.layout},{label:`Compile`,tone:h(r.metadata.status),value:r.metadata.status},{label:`Diagnostics`,tone:r.metadata.discovery.summary.errors>0?`danger`:r.metadata.discovery.summary.warnings>0?`warning`:`success`,value:m(r.metadata.discovery.summary.errors,r.metadata.discovery.summary.warnings)},{label:`Instructions`,value:r.manifest.instructions?.logicalPath??`none`},{label:`Skills`,value:p(r.manifest.skills.length,`skill`)}),s.unshift({label:`Compiled Manifest`,value:r.paths.compiledManifestPath},{label:`Discovery Manifest`,value:r.paths.discoveryManifestPath},{label:`Diagnostics`,value:r.paths.diagnosticsPath},{label:`Module Map`,value:r.paths.moduleMapPath},{label:`Metadata`,value:r.paths.compileMetadataPath}),f.push(r.manifest.instructions===void 0?{label:`Instructions`,value:`No instructions prompt discovered.`}:{label:`Instructions`,value:r.manifest.instructions.logicalPath})),e.log([l(a,{subtitle:`Resolved application paths and the active message contract.`,title:`Ash Info`}),``,u(a,{rows:o,title:`Application`}),``,u(a,{rows:s,title:`Artifacts`}),...r===null?[]:[``,u(a,{rows:f,title:`Instructions`})],``,u(a,{rows:[{label:`Workflow ID`,value:i.workflowId},{label:`Source Dir`,value:i.workflowSourceDir},{label:`Create`,tone:`info`,value:`POST ${n.messaging.createSessionRoutePath}`},{label:`Continue`,tone:`info`,value:`POST ${n.messaging.continueSessionRoutePattern}`},{label:`Stream`,tone:`info`,value:`GET ${n.messaging.streamRoutePattern}`}],title:`Messaging`})].join(`
1
+ import{D as e,b as t,t as n,v as r,y as i}from"../../chunks/paths-B-Onq-sx.js";import{d as a,f as o,h as s}from"../../chunks/types-MZUhN0Zy.js";import{createCliTheme as c,renderCliBanner as l,renderCliSection as u}from"../ui/output.js";async function d(e){let t=await f(e);return{application:n(t?.project.appRoot??e),compiledState:t,messaging:{createSessionRoutePath:o,continueSessionRoutePattern:a,streamRoutePattern:s}}}async function f(n){try{return await i({startPath:n})}catch(n){if(n instanceof r)return n.result;if(n instanceof e||n instanceof t)return null;throw n}}function p(e,t){return`${e} ${t}${e===1?``:`s`}`}function m(e,t){return`${`${e} error${e===1?``:`s`}`}, ${`${t} warning${t===1?``:`s`}`}`}function h(e){switch(e){case`ready`:return`success`;case`failed`:return`danger`;default:return`warning`}}async function g(e,t){let n=await d(t),r=n.compiledState,i=n.application,a=c(),o=[{label:`App Root`,value:i.appRoot}],s=[{label:`Workflow Build`,value:i.workflowBuildDir},{label:`Output`,value:i.outputDir}],f=[];r===null?o.push({label:`Compile`,tone:`warning`,value:`unavailable`}):(o.push({label:`Agent Root`,value:r.project.agentRoot},{label:`Layout`,value:r.project.layout},{label:`Compile`,tone:h(r.metadata.status),value:r.metadata.status},{label:`Diagnostics`,tone:r.metadata.discovery.summary.errors>0?`danger`:r.metadata.discovery.summary.warnings>0?`warning`:`success`,value:m(r.metadata.discovery.summary.errors,r.metadata.discovery.summary.warnings)},{label:`Instructions`,value:r.manifest.instructions?.logicalPath??`none`},{label:`Skills`,value:p(r.manifest.skills.length,`skill`)}),s.unshift({label:`Compiled Manifest`,value:r.paths.compiledManifestPath},{label:`Discovery Manifest`,value:r.paths.discoveryManifestPath},{label:`Diagnostics`,value:r.paths.diagnosticsPath},{label:`Module Map`,value:r.paths.moduleMapPath},{label:`Metadata`,value:r.paths.compileMetadataPath}),f.push(r.manifest.instructions===void 0?{label:`Instructions`,value:`No instructions prompt discovered.`}:{label:`Instructions`,value:r.manifest.instructions.logicalPath})),e.log([l(a,{subtitle:`Resolved application paths and the active message contract.`,title:`Ash Info`}),``,u(a,{rows:o,title:`Application`}),``,u(a,{rows:s,title:`Artifacts`}),...r===null?[]:[``,u(a,{rows:f,title:`Instructions`})],``,u(a,{rows:[{label:`Workflow ID`,value:i.workflowId},{label:`Source Dir`,value:i.workflowSourceDir},{label:`Create`,tone:`info`,value:`POST ${n.messaging.createSessionRoutePath}`},{label:`Continue`,tone:`info`,value:`POST ${n.messaging.continueSessionRoutePattern}`},{label:`Stream`,tone:`info`,value:`GET ${n.messaging.streamRoutePattern}`}],title:`Messaging`})].join(`
2
2
  `))}export{g as printApplicationInfo};
@@ -1,3 +1,3 @@
1
- import{t as e}from"../chunks/package-DmsQgn4v.js";import{createCliTheme as t,renderCliTaggedLine as n}from"./ui/output.js";import{i as r,n as i,r as a,t as o}from"../chunks/url-BVRhVE2O.js";import{resolve as s}from"node:path";async function c(){return(await import(`../chunks/host-DMccRKcz.js`).then(e=>e.t)).buildHost}async function l(){return(await import(`./commands/info.js`)).printApplicationInfo}async function u(){return(await import(`./dev/repl.js`)).runDevelopmentRepl}async function d(){return(await import(`../evals/cli/eval.js`)).runEvalCommand}async function f(){return(await import(`../chunks/host-DMccRKcz.js`).then(e=>e.t)).startHost}function p(e=process.cwd()){return s(e)}function m(e){return`Ash (v${e})`}function h(e){return e.name()===`info`||e.name()===`dev`}async function g(e){await new Promise((t,n)=>{let r=!1,i=()=>{process.off(`SIGINT`,a),process.off(`SIGTERM`,a)},a=()=>{r||(r=!0,i(),e.close().then(t,n))};process.once(`SIGINT`,a),process.once(`SIGTERM`,a)})}function _(e){if(!/^-?\d+$/.test(e))throw new r(`Expected a numeric port, received "${e}".`);let t=Number(e);if(!Number.isInteger(t))throw new r(`Expected a numeric port, received "${e}".`);if(t<0||t>65535)throw new r(`Expected a port between 0 and 65535, received "${e}".`);return t}function v(){return!!(process.stdin.isTTY&&process.stdout.isTTY)}function y(e){let t=e[1];return e[0]!==`dev`||e.length!==2||t===void 0||t.startsWith(`-`)?[...e]:[`dev`,`--url`,t]}function b(e){if(e.url){if(e.host!==void 0)throw new r(`The --host option cannot be used with --url.`);if(e.port!==void 0)throw new r(`The --port option cannot be used with --url.`);if(e.repl===!1)throw new r(`The --no-repl option cannot be used with --url.`);return e.url}}function x(r,a){let s=p(),y=e().version,x=new i,S=t();return x.name(`ash`).description(`Build and run an Ash application.`).version(y).showHelpAfterError().exitOverride().hook(`preAction`,(e,t)=>{h(t)&&r.log(m(y))}).configureOutput({writeErr:e=>{r.error(e.trimEnd())},writeOut:e=>{r.log(e.trimEnd())}}),x.command(`build`).description(`Build the current Ash application.`).action(async()=>{let{loadDevelopmentEnvironmentFiles:e}=await import(`./dev/environment.js`);e(s);let t=await(a.buildHost??await c())(s);r.log(n(S,{message:`built output at ${t}`,tag:`build`,tone:`success`}))}),x.command(`dev`).description(`Start the Ash development server or connect the REPL to an existing URL.`).option(`--host <host>`,`Host interface to bind`).option(`--no-repl`,`Start the server without the interactive REPL`).option(`--port <port>`,`Port to listen on (defaults to $PORT, then 3000)`,_).option(`--schedules`,`Run scheduled tasks during development (off by default)`).option(`-u, --url <url>`,`Connect the REPL to an existing server URL`,o).addHelpText(`after`,`
1
+ import{t as e}from"../chunks/package-DmsQgn4v.js";import{createCliTheme as t,renderCliTaggedLine as n}from"./ui/output.js";import{i as r,n as i,r as a,t as o}from"../chunks/url-BVRhVE2O.js";import{resolve as s}from"node:path";async function c(){return(await import(`../chunks/host-M56X595D.js`).then(e=>e.t)).buildHost}async function l(){return(await import(`./commands/info.js`)).printApplicationInfo}async function u(){return(await import(`./dev/repl.js`)).runDevelopmentRepl}async function d(){return(await import(`../evals/cli/eval.js`)).runEvalCommand}async function f(){return(await import(`../chunks/host-M56X595D.js`).then(e=>e.t)).startHost}function p(e=process.cwd()){return s(e)}function m(e){return`Ash (v${e})`}function h(e){return e.name()===`info`||e.name()===`dev`}async function g(e){await new Promise((t,n)=>{let r=!1,i=()=>{process.off(`SIGINT`,a),process.off(`SIGTERM`,a)},a=()=>{r||(r=!0,i(),e.close().then(t,n))};process.once(`SIGINT`,a),process.once(`SIGTERM`,a)})}function _(e){if(!/^-?\d+$/.test(e))throw new r(`Expected a numeric port, received "${e}".`);let t=Number(e);if(!Number.isInteger(t))throw new r(`Expected a numeric port, received "${e}".`);if(t<0||t>65535)throw new r(`Expected a port between 0 and 65535, received "${e}".`);return t}function v(){return!!(process.stdin.isTTY&&process.stdout.isTTY)}function y(e){let t=e[1];return e[0]!==`dev`||e.length!==2||t===void 0||t.startsWith(`-`)?[...e]:[`dev`,`--url`,t]}function b(e){if(e.url){if(e.host!==void 0)throw new r(`The --host option cannot be used with --url.`);if(e.port!==void 0)throw new r(`The --port option cannot be used with --url.`);if(e.repl===!1)throw new r(`The --no-repl option cannot be used with --url.`);return e.url}}function x(r,a){let s=p(),y=e().version,x=new i,S=t();return x.name(`ash`).description(`Build and run an Ash application.`).version(y).showHelpAfterError().exitOverride().hook(`preAction`,(e,t)=>{h(t)&&r.log(m(y))}).configureOutput({writeErr:e=>{r.error(e.trimEnd())},writeOut:e=>{r.log(e.trimEnd())}}),x.command(`build`).description(`Build the current Ash application.`).action(async()=>{let{loadDevelopmentEnvironmentFiles:e}=await import(`./dev/environment.js`);e(s);let t=await(a.buildHost??await c())(s);r.log(n(S,{message:`built output at ${t}`,tag:`build`,tone:`success`}))}),x.command(`dev`).description(`Start the Ash development server or connect the REPL to an existing URL.`).option(`--host <host>`,`Host interface to bind`).option(`--no-repl`,`Start the server without the interactive REPL`).option(`--port <port>`,`Port to listen on (defaults to $PORT, then 3000)`,_).option(`--schedules`,`Run scheduled tasks during development (off by default)`).option(`-u, --url <url>`,`Connect the REPL to an existing server URL`,o).addHelpText(`after`,`
2
2
  You can also pass a bare URL as the only argument, for example: ash dev https://example.com
3
3
  `).action(async e=>{let t=b(e),{loadDevelopmentEnvironmentFiles:i}=await import(`./dev/environment.js`);if(i(s),t){if(r.log(n(S,{message:`REPL connecting to ${t}`,tag:`dev`,tone:`info`})),!v()){r.log(n(S,{message:`Interactive REPL disabled because the current terminal is not a TTY.`,tag:`dev`,tone:`warning`}));return}r.log(``),await(a.runDevelopmentRepl??await u())({serverUrl:t});return}let o=await(a.startHost??await f())(s,{host:e.host,port:e.port,schedules:e.schedules===!0}),c=!1,l=async()=>{c||(c=!0,await o.close())};try{if(r.log(n(S,{message:`server listening at ${o.url}`,tag:`dev`,tone:`success`})),e.repl===!1)return await g({close:l});if(!v())return r.log(n(S,{message:`Interactive REPL disabled because the current terminal is not a TTY.`,tag:`dev`,tone:`warning`})),await g({close:l});r.log(``),await(a.runDevelopmentRepl??await u())({serverUrl:o.url})}finally{await l()}}),x.command(`info`).description(`Print resolved application information.`).action(async()=>{await(a.printApplicationInfo??await l())(r,s)}),x.command(`eval`).description(`Run eval suites against an Ash agent.`).option(`--suite <id...>`,`Suite IDs to run (repeatable)`).option(`--all`,`Run all discovered suites`).option(`--url <url>`,`Remote agent URL (skip local host startup)`).option(`--timeout <ms>`,`Per-case timeout in milliseconds`).option(`--max-concurrency <n>`,`Max concurrent case executions per suite`).option(`--json`,`Output results as JSON`).option(`--list-suites`,`List discovered suites and exit`).option(`--skip-report`,`Skip suite-defined reporters (e.g. Braintrust)`).action(async e=>{await(a.runEvalCommand??await d())(e,r)}),x}async function S(e=process.argv.slice(2),t=console,n={}){let r=x(t,n),i=e.length===0?[`info`]:y(e);try{await r.parseAsync(i,{from:`user`})}catch(e){if(e instanceof a){if(e.exitCode===0)return;throw Error(e.message)}throw e}}export{S as runCli};
@@ -1 +1 @@
1
- import{n as e}from"../../chunks/paths-B-aiDznc.js";import{loadDevelopmentEnvironmentFiles as t}from"../../cli/dev/environment.js";import{a as n,n as r,t as i}from"../../chunks/client-BeZ_W7vl.js";import{n as a}from"../../chunks/host-DMccRKcz.js";import{discoverAndImportSuites as o,discoverSuiteFiles as s,importSuiteFile as c}from"../runner/discover.js";import{executeSuite as l}from"../runner/execute-suite.js";import{ConsoleReporter as u}from"../runner/reporters/console.js";var d=n();function f(e,t){e.command(`eval`).description(`Run eval suites against an Ash agent.`).option(`--suite <id...>`,`Suite IDs to run (repeatable)`).option(`--all`,`Run all discovered suites`).option(`--url <url>`,`Remote agent URL (skip local host startup)`).option(`--timeout <ms>`,`Per-case timeout in milliseconds`).option(`--max-concurrency <n>`,`Max concurrent case executions per suite`).option(`--json`,`Output results as JSON`).option(`--list-suites`,`List discovered suites and exit`).option(`--skip-report`,`Skip suite-defined reporters (e.g. Braintrust)`).action(async e=>{await p(e,t)})}async function p(n,r){let i=e();if(t(i),n.listSuites){await y(i,r);return}let s=n.suite,c=await o(i,s);if(c.length===0){s&&s.length>0?r.error(`No suites found matching: ${s.join(`, `)}`):r.error(`No eval suites found. Create suite files under evals/ with the *.eval.ts extension.`),process.exitCode=1;return}let u,d;n.url?d={kind:`remote`,url:n.url}:(u=await a(i,{host:`127.0.0.1`,port:0}),d={kind:`local`,url:u.url});let f=m(d);try{let e=[];for(let t of c){let r=_(t,n),a=v(r,{json:n.json===!0,skipReport:n.skipReport===!0}),o=await l({suite:r,target:d,reporters:a,appRoot:i,client:f});e.push(o)}n.json&&r.log(JSON.stringify(e,null,2)),e.some(e=>e.errored>0)&&(process.exitCode=1)}finally{u&&await u.close()}process.exit(process.exitCode??0)}function m(e){if(e.kind===`local`)return new i({host:e.url});let t={},n=process.env.VERCEL_AUTOMATION_BYPASS_SECRET?.trim();return n&&(t[r]=n),new i({auth:h(),headers:Object.keys(t).length>0?t:void 0,host:e.url})}function h(){let e=process.env.ASH_EVAL_AUTH_TOKEN?.trim();return e?{bearer:e}:{bearer:g}}async function g(){try{let e=(await(0,d.getVercelOidcToken)()).trim();if(e.length>0)return e}catch{}return process.env.VERCEL_OIDC_TOKEN?.trim()??``}function _(e,t){let n=t.maxConcurrency?Number.parseInt(t.maxConcurrency,10):void 0,r=t.timeout?Number.parseInt(t.timeout,10):void 0;if(n===void 0&&r===void 0)return e;let i={...e};return n!==void 0&&(i.maxConcurrency=n),r!==void 0&&(i.timeoutMs=r),i}function v(e,t){let n=t.json?[]:[new u];return!t.skipReport&&e.reporters&&n.push(...e.reporters),n}async function y(e,t){let n=await s(e);if(n.length===0){t.log(`No eval suites found.`);return}t.log(`Found ${n.length} eval suite file(s):\n`);for(let r of n){let n=await c(e,r);t.log(` ${n.id}${n.description?` - ${n.description}`:``}`)}}export{f as registerEvalCommand,p as runEvalCommand};
1
+ import{n as e}from"../../chunks/paths-B-Onq-sx.js";import{loadDevelopmentEnvironmentFiles as t}from"../../cli/dev/environment.js";import{a as n,n as r,t as i}from"../../chunks/client-BeZ_W7vl.js";import{n as a}from"../../chunks/host-M56X595D.js";import{discoverAndImportSuites as o,discoverSuiteFiles as s,importSuiteFile as c}from"../runner/discover.js";import{executeSuite as l}from"../runner/execute-suite.js";import{ConsoleReporter as u}from"../runner/reporters/console.js";var d=n();function f(e,t){e.command(`eval`).description(`Run eval suites against an Ash agent.`).option(`--suite <id...>`,`Suite IDs to run (repeatable)`).option(`--all`,`Run all discovered suites`).option(`--url <url>`,`Remote agent URL (skip local host startup)`).option(`--timeout <ms>`,`Per-case timeout in milliseconds`).option(`--max-concurrency <n>`,`Max concurrent case executions per suite`).option(`--json`,`Output results as JSON`).option(`--list-suites`,`List discovered suites and exit`).option(`--skip-report`,`Skip suite-defined reporters (e.g. Braintrust)`).action(async e=>{await p(e,t)})}async function p(n,r){let i=e();if(t(i),n.listSuites){await y(i,r);return}let s=n.suite,c=await o(i,s);if(c.length===0){s&&s.length>0?r.error(`No suites found matching: ${s.join(`, `)}`):r.error(`No eval suites found. Create suite files under evals/ with the *.eval.ts extension.`),process.exitCode=1;return}let u,d;n.url?d={kind:`remote`,url:n.url}:(u=await a(i,{host:`127.0.0.1`,port:0}),d={kind:`local`,url:u.url});let f=m(d);try{let e=[];for(let t of c){let r=_(t,n),a=v(r,{json:n.json===!0,skipReport:n.skipReport===!0}),o=await l({suite:r,target:d,reporters:a,appRoot:i,client:f});e.push(o)}n.json&&r.log(JSON.stringify(e,null,2)),e.some(e=>e.errored>0)&&(process.exitCode=1)}finally{u&&await u.close()}process.exit(process.exitCode??0)}function m(e){if(e.kind===`local`)return new i({host:e.url});let t={},n=process.env.VERCEL_AUTOMATION_BYPASS_SECRET?.trim();return n&&(t[r]=n),new i({auth:h(),headers:Object.keys(t).length>0?t:void 0,host:e.url})}function h(){let e=process.env.ASH_EVAL_AUTH_TOKEN?.trim();return e?{bearer:e}:{bearer:g}}async function g(){try{let e=(await(0,d.getVercelOidcToken)()).trim();if(e.length>0)return e}catch{}return process.env.VERCEL_OIDC_TOKEN?.trim()??``}function _(e,t){let n=t.maxConcurrency?Number.parseInt(t.maxConcurrency,10):void 0,r=t.timeout?Number.parseInt(t.timeout,10):void 0;if(n===void 0&&r===void 0)return e;let i={...e};return n!==void 0&&(i.maxConcurrency=n),r!==void 0&&(i.timeoutMs=r),i}function v(e,t){let n=t.json?[]:[new u];return!t.skipReport&&e.reporters&&n.push(...e.reporters),n}async function y(e,t){let n=await s(e);if(n.length===0){t.log(`No eval suites found.`);return}t.log(`Found ${n.length} eval suite file(s):\n`);for(let r of n){let n=await c(e,r);t.log(` ${n.id}${n.description?` - ${n.description}`:``}`)}}export{f as registerEvalCommand,p as runEvalCommand};
@@ -6,7 +6,7 @@ import { ASH_PACKAGE_NAME } from "#package-name.js";
6
6
  let cachedPackageInfo;
7
7
  // The package build stamps the published version into `dist` so bundled
8
8
  // deployments can still report package metadata without resolving package.json.
9
- const BUNDLED_FALLBACK_PACKAGE_VERSION = "0.8.0";
9
+ const BUNDLED_FALLBACK_PACKAGE_VERSION = "0.8.2";
10
10
  const BUNDLED_FALLBACK_PACKAGE_VERSION_PLACEHOLDER = "__ASH_PACKAGE_VERSION_PLACEHOLDER__";
11
11
  const WORKFLOW_MODULE_ALIASES = {
12
12
  "workflow/api": "src/compiled/@workflow/core/runtime.js",
@@ -13,6 +13,16 @@ const KNOWN_AUTHORIZATION_KEYS = [
13
13
  "getToken",
14
14
  "principalType",
15
15
  "startAuthorization",
16
+ // Optional metadata marker that auth-provider helpers may attach so
17
+ // downstream tooling (eg. the Ash compiler / Vercel dashboard) can
18
+ // detect Vercel Connect-backed connections without opening the
19
+ // closure state of `getToken`. The runtime never reads it; it
20
+ // survives `normalizeAuthorizationSpec` so consumers can pick it
21
+ // off the normalized auth definition. See
22
+ // `runtime/connections/types.ts#AuthorizationDefinitionBase` for
23
+ // the type and `@vercel/connect/ash`'s `connect()` for the
24
+ // canonical producer.
25
+ "vercelConnect",
16
26
  ];
17
27
  /**
18
28
  * Validates one authored MCP client connection module export at build time
@@ -5,4 +5,11 @@ export interface AshChannelInput {
5
5
  readonly auth: AuthFn<Request>;
6
6
  readonly uploadPolicy?: Partial<UploadPolicy>;
7
7
  }
8
- export declare function ashChannel(input: AshChannelInput): Channel;
8
+ /**
9
+ * Concrete return type of {@link ashChannel}. Named so consumers can
10
+ * default-export an `ashChannel(...)` call under `declaration: true`
11
+ * without TypeScript falling back to an internal path for `Channel`.
12
+ */
13
+ export interface AshChannel extends Channel {
14
+ }
15
+ export declare function ashChannel(input: AshChannelInput): AshChannel;
@@ -1,4 +1,4 @@
1
1
  export { slack, type SlackOptions } from "#public/channels/slack/slack.js";
2
- export { slackChannel, type SlackApiHandle, type SlackApiResponse, type SlackChannelConfig, type SlackChannelEvents, type SlackChannelCredentials, type SlackChannelState, type SlackContext, type SlackInteractionAction, type SlackReceiveArgs, } from "#public/channels/slack/slackChannel.js";
2
+ export { slackChannel, type SlackApiHandle, type SlackApiResponse, type SlackChannel, type SlackChannelConfig, type SlackChannelEvents, type SlackChannelCredentials, type SlackChannelState, type SlackContext, type SlackInteractionAction, type SlackReceiveArgs, } from "#public/channels/slack/slackChannel.js";
3
3
  export { Actions, Button, Card, CardText, Divider, Fields, Image, LinkButton, Modal, RadioSelect, Section, Select, SelectOption, Table, TextInput, } from "#compiled/chat/index.js";
4
4
  export type { AdapterPostableMessage, Attachment, Author, CardElement, FileUpload, Message, PostableMessage, SentMessage, Thread, } from "#compiled/chat/index.js";
@@ -19,21 +19,21 @@ export function slack(options = {}) {
19
19
  return { auth: resolveAuth(message) };
20
20
  },
21
21
  events: {
22
- "turn.started"(_event, ctx) {
23
- ctx.thread.startTyping("Thinking...");
22
+ async "turn.started"(_event, ctx) {
23
+ await ctx.thread.startTyping("Thinking...");
24
24
  },
25
- "actions.requested"(event, ctx) {
25
+ async "actions.requested"(event, ctx) {
26
26
  const labels = event.actions.map((a) => (a.kind === "tool-call" ? a.toolName : a.kind));
27
- ctx.thread.startTyping(`Running ${labels.join(", ")}...`);
27
+ await ctx.thread.startTyping(`Running ${labels.join(", ")}...`);
28
28
  },
29
- "message.completed"(event, ctx) {
29
+ async "message.completed"(event, ctx) {
30
30
  if (event.finishReason === "tool-calls")
31
31
  return;
32
32
  if (event.message)
33
- ctx.thread.post(event.message);
33
+ await ctx.thread.post(event.message);
34
34
  },
35
- "session.failed"(_event, ctx) {
36
- ctx.thread.post("Something went wrong.");
35
+ async "session.failed"(_event, ctx) {
36
+ await ctx.thread.post("Something went wrong.");
37
37
  },
38
38
  },
39
39
  });
@@ -1,5 +1,5 @@
1
- import type { SlackBotToken } from "#compiled/@chat-adapter/slack/index.js";
2
- import type { Message, SerializedThread, Thread } from "#compiled/chat/index.js";
1
+ import { type SlackBotToken } from "#compiled/@chat-adapter/slack/index.js";
2
+ import { type Message, type SerializedThread, type Thread } from "#compiled/chat/index.js";
3
3
  import type { HandleMessageStreamEvent } from "#protocol/message.js";
4
4
  import { type UploadPolicy } from "#public/channels/upload-policy.js";
5
5
  import { type Channel } from "#public/definitions/defineChannel.js";
@@ -72,6 +72,10 @@ export interface SlackChannelEvents {
72
72
  export interface SlackChannelConfig {
73
73
  readonly credentials?: SlackChannelCredentials;
74
74
  readonly botName?: string;
75
+ /**
76
+ * Override the default webhook route path (`/ash/v1/slack`).
77
+ */
78
+ readonly route?: string;
75
79
  /**
76
80
  * Inbound upload policy applied to file attachments before they
77
81
  * reach the harness. Violating attachments are dropped with a
@@ -83,5 +87,12 @@ export interface SlackChannelConfig {
83
87
  onInteraction?(action: SlackInteractionAction, ctx: SlackContext): void | Promise<void>;
84
88
  readonly events?: SlackChannelEvents;
85
89
  }
86
- export declare function slackChannel(config?: SlackChannelConfig): Channel<SlackChannelState>;
90
+ /**
91
+ * Concrete return type of {@link slackChannel}. Named so consumers can
92
+ * default-export a `slackChannel(...)` call under `declaration: true`
93
+ * without TypeScript falling back to an internal path for `Channel`.
94
+ */
95
+ export interface SlackChannel extends Channel<SlackChannelState> {
96
+ }
97
+ export declare function slackChannel(config?: SlackChannelConfig): SlackChannel;
87
98
  export {};
@@ -1,8 +1,10 @@
1
+ import { createSlackAdapter } from "#compiled/@chat-adapter/slack/index.js";
2
+ import { ThreadImpl, } from "#compiled/chat/index.js";
1
3
  import { createLogger } from "#internal/logging.js";
2
4
  import { buildSlackTurnMessage, collectSlackFileParts, createSlackFetchFile, } from "#public/channels/slack/attachments.js";
3
5
  import { deriveHitlResponse, isHitlAction, renderInputRequestBlocks, } from "#public/channels/slack/hitl.js";
4
6
  import { mergeUploadPolicy } from "#public/channels/upload-policy.js";
5
- import { defineChannel, POST, } from "#public/definitions/defineChannel.js";
7
+ import { defineChannel, POST } from "#public/definitions/defineChannel.js";
6
8
  const log = createLogger("slack.channel");
7
9
  function decodeThreadId(id) {
8
10
  const parts = id.replace(/^slack:/u, "").split(":");
@@ -74,15 +76,18 @@ function extractSelectedOptionValue(action) {
74
76
  return typeof selected?.value === "string" ? selected.value : undefined;
75
77
  }
76
78
  function rebuildSlackContext(state, botToken) {
77
- const chatModule = require("#compiled/chat/index.js");
79
+ const adapter = createSlackAdapter({
80
+ botToken: botToken ?? process.env.SLACK_BOT_TOKEN,
81
+ });
78
82
  const thread = state.serializedThread
79
- ? chatModule.ThreadImpl.fromJSON(state.serializedThread)
80
- : new chatModule.ThreadImpl({
83
+ ? ThreadImpl.fromJSON(state.serializedThread, adapter)
84
+ : new ThreadImpl({
81
85
  adapterName: "slack",
82
86
  channelId: "",
83
87
  id: "slack::",
84
88
  isDM: false,
85
89
  });
90
+ thread._adapter = adapter;
86
91
  return {
87
92
  thread,
88
93
  slack: buildSlackApiHandle(thread, botToken, state.teamId ?? undefined),
@@ -112,7 +117,6 @@ export function slackChannel(config = {}) {
112
117
  const slackFetchFile = createSlackFetchFile({
113
118
  botToken: config.credentials?.botToken,
114
119
  });
115
- let activeSend = null;
116
120
  let chatPromise = null;
117
121
  async function getChat() {
118
122
  if (chatPromise)
@@ -139,39 +143,6 @@ export function slackChannel(config = {}) {
139
143
  userName: config.botName ?? "ash-agent",
140
144
  });
141
145
  await chat.initialize();
142
- chat.onNewMention(async (thread, message) => {
143
- // Slack sends both `app_mention` and `message.channels` for the same message.
144
- // The Chat SDK dedup relies on in-memory state that doesn't survive across
145
- // serverless invocations, so both events reach this handler. Only process
146
- // `app_mention` to prevent duplicate runs.
147
- const rawEvent = message.raw;
148
- if (rawEvent?.type !== "app_mention")
149
- return;
150
- const send = activeSend;
151
- if (!send) {
152
- throw new Error("slackChannel: mention received but no request context is active.");
153
- }
154
- const teamId = rawEvent.team_id ?? rawEvent.team;
155
- const slackCtx = {
156
- thread,
157
- slack: buildSlackApiHandle(thread, config.credentials?.botToken, teamId),
158
- };
159
- const runOpts = config.run ? config.run(slackCtx, message) : { auth: null };
160
- if (runOpts === null)
161
- return;
162
- const decoded = decodeThreadId(thread.id ?? "");
163
- const continuationToken = `slack:${decoded.channelId}:${decoded.threadTs}`;
164
- const fileParts = collectSlackFileParts(message, uploadPolicy);
165
- const turnMessage = buildSlackTurnMessage(message.text, fileParts);
166
- await send(turnMessage, {
167
- auth: runOpts.auth,
168
- continuationToken,
169
- state: {
170
- serializedThread: thread.toJSON(),
171
- teamId: teamId ?? null,
172
- },
173
- });
174
- });
175
146
  return { chat };
176
147
  })();
177
148
  chatPromise = promise;
@@ -188,7 +159,7 @@ export function slackChannel(config = {}) {
188
159
  return rebuildSlackContext(state, config.credentials?.botToken);
189
160
  },
190
161
  routes: [
191
- POST("/ash/v1/slack", async (req, { send, waitUntil }) => {
162
+ POST(config.route ?? "/ash/v1/slack", async (req, { send, waitUntil }) => {
192
163
  const { chat } = await getChat();
193
164
  const contentType = req.headers.get("content-type") ?? "";
194
165
  if (contentType.includes("application/x-www-form-urlencoded")) {
@@ -249,16 +220,36 @@ export function slackChannel(config = {}) {
249
220
  }
250
221
  return new Response("ok", { status: 200 });
251
222
  }
252
- activeSend = send;
253
- try {
254
- const webhookHandler = chat.webhooks.slack;
255
- return await webhookHandler(req, {
256
- waitUntil,
223
+ chat.onNewMention(async (thread, message) => {
224
+ const rawEvent = message.raw;
225
+ // Slack sends both `app_mention` and `message.channels` for the same
226
+ // utterance. The Chat SDK dedup relies on in-memory state that doesn't
227
+ // survive serverless invocations, so both events reach this handler.
228
+ // Only process `app_mention` to prevent duplicate runs.
229
+ if (rawEvent?.type !== "app_mention")
230
+ return;
231
+ const teamId = rawEvent.team_id ?? rawEvent.team;
232
+ const slackCtx = {
233
+ thread,
234
+ slack: buildSlackApiHandle(thread, config.credentials?.botToken, teamId),
235
+ };
236
+ const runOpts = config.run ? config.run(slackCtx, message) : { auth: null };
237
+ if (runOpts === null)
238
+ return;
239
+ const decoded = decodeThreadId(thread.id ?? "");
240
+ const continuationToken = `slack:${decoded.channelId}:${decoded.threadTs}`;
241
+ const fileParts = collectSlackFileParts(message, uploadPolicy);
242
+ const turnMessage = buildSlackTurnMessage(message.text, fileParts);
243
+ await send(turnMessage, {
244
+ auth: runOpts.auth,
245
+ continuationToken,
246
+ state: {
247
+ serializedThread: thread.toJSON(),
248
+ teamId: teamId ?? null,
249
+ },
257
250
  });
258
- }
259
- finally {
260
- activeSend = null;
261
- }
251
+ });
252
+ return await chat.webhooks.slack(req, { waitUntil });
262
253
  }),
263
254
  ],
264
255
  async receive(input, { send }) {
@@ -127,6 +127,27 @@ interface AuthorizationDefinitionBase {
127
127
  * full design.
128
128
  */
129
129
  readonly principalType: "app" | "user";
130
+ /**
131
+ * Optional metadata marker attached by `connect()` from
132
+ * `@vercel/connect/ash` so downstream tooling — eg. a future Ash
133
+ * compiler step that surfaces connector identifiers in build output,
134
+ * or the Vercel dashboard rendering deep links to a connector's
135
+ * settings page — can detect Vercel Connect-backed connections at
136
+ * compile time without inspecting `getToken`'s closure state.
137
+ *
138
+ * The runtime token-fetch path ignores this field; it is purely
139
+ * provider attribution. Authors writing their own `getToken`
140
+ * callbacks (raw bearer tokens, custom callbacks) should leave it
141
+ * unset.
142
+ *
143
+ * `connector` carries whatever value the author passed to
144
+ * `connect()` — UID like `"oauth/mcp-linear-app"` or opaque
145
+ * `"scl_..."`; both forms address the same connector on the Vercel
146
+ * Connect side.
147
+ */
148
+ readonly vercelConnect?: {
149
+ readonly connector: string;
150
+ };
130
151
  }
131
152
  /**
132
153
  * Non-interactive authorization: the runtime only ever calls
@@ -67,17 +67,42 @@ export function normalizeAuthorizationSpec(authorization, prefix, fieldName = "a
67
67
  throw new Error(`${prefix} ${message}`);
68
68
  }
69
69
  const auth = authorization;
70
+ const vercelConnect = extractVercelConnectMarker(auth.vercelConnect);
70
71
  if (auth.startAuthorization !== undefined && auth.completeAuthorization !== undefined) {
71
- return {
72
+ const interactive = {
72
73
  completeAuthorization: auth.completeAuthorization,
73
74
  getToken: auth.getToken,
74
75
  principalType: "user",
75
76
  startAuthorization: auth.startAuthorization,
76
77
  };
78
+ return vercelConnect === undefined ? interactive : { ...interactive, vercelConnect };
77
79
  }
78
- return {
80
+ const nonInteractive = {
79
81
  getToken: auth.getToken,
80
82
  principalType: (auth.principalType ??
81
83
  "app"),
82
84
  };
85
+ return vercelConnect === undefined ? nonInteractive : { ...nonInteractive, vercelConnect };
86
+ }
87
+ /**
88
+ * Reads the optional `vercelConnect: { connector: string }` marker
89
+ * attached by `@vercel/connect/ash`'s `connect()` helper. Returns the
90
+ * parsed marker when present and well-formed, otherwise `undefined`.
91
+ *
92
+ * The marker is opaque to the runtime — it exists so downstream tooling
93
+ * (eg. the Ash compiler / Vercel dashboard) can attribute the auth
94
+ * back to a Vercel Connect connector without inspecting `getToken`'s
95
+ * closure state. Validation is lenient (a malformed marker is dropped,
96
+ * not thrown) so a misbehaving auth provider can't fail an otherwise-
97
+ * valid connection.
98
+ */
99
+ function extractVercelConnectMarker(value) {
100
+ if (value === null || typeof value !== "object") {
101
+ return undefined;
102
+ }
103
+ const connector = value.connector;
104
+ if (typeof connector !== "string" || connector.length === 0) {
105
+ return undefined;
106
+ }
107
+ return { connector };
83
108
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "experimental-ash",
3
- "version": "0.8.0",
3
+ "version": "0.8.2",
4
4
  "bin": {
5
5
  "ash": "./bin/ash.js",
6
6
  "experimental-ash": "./bin/ash.js"