experimental-ash 0.7.1 → 0.7.3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/docs/external-agent-protocol.md +5 -5
- package/dist/docs/internals/README.md +8 -8
- package/dist/docs/internals/context.md +1 -1
- package/dist/docs/internals/hooks.md +5 -5
- package/dist/docs/internals/message-runtime.md +6 -6
- package/dist/docs/public/README.md +22 -22
- package/dist/docs/public/agent-ts.md +21 -17
- package/dist/docs/public/auth-and-route-protection.md +13 -13
- package/dist/docs/public/channels/README.md +16 -13
- package/dist/docs/public/cli-build-and-debugging.md +3 -3
- package/dist/docs/public/connections.md +5 -5
- package/dist/docs/public/context-control.md +8 -8
- package/dist/docs/public/evals.md +3 -3
- package/dist/docs/public/getting-started.md +5 -5
- package/dist/docs/public/hooks.md +4 -4
- package/dist/docs/public/human-in-the-loop.md +3 -3
- package/dist/docs/public/instrumentation.md +4 -3
- package/dist/docs/public/project-layout.md +11 -11
- package/dist/docs/public/runs-and-streaming.md +3 -3
- package/dist/docs/public/sandbox.md +3 -3
- package/dist/docs/public/session-context.md +10 -6
- package/dist/docs/public/skills.md +5 -3
- package/dist/docs/public/subagents.md +2 -2
- package/dist/docs/public/tools.md +4 -5
- package/dist/docs/public/typescript-api.md +12 -12
- package/dist/docs/public/vercel-deployment.md +4 -4
- package/dist/docs/public/workspace.md +3 -3
- package/dist/src/channel/compiled-channel.d.ts +4 -2
- package/dist/src/channel/schedule.d.ts +11 -23
- package/dist/src/channel/schedule.js +18 -19
- package/dist/src/chunks/{dev-authored-source-watcher-CYsfBiYM.js → dev-authored-source-watcher-Druw92QN.js} +1 -1
- package/dist/src/chunks/{host-CsF9KDv8.js → host-CQ7AZID3.js} +2 -2
- package/dist/src/chunks/{paths-DvimrhJF.js → paths-DQbfjCIS.js} +25 -25
- package/dist/src/chunks/{prewarm-DdHk68ib.js → prewarm-CcphIXc0.js} +1 -1
- package/dist/src/cli/commands/info.js +1 -1
- package/dist/src/cli/run.js +1 -1
- package/dist/src/compiler/normalize-channel.d.ts +4 -4
- package/dist/src/compiler/normalize-channel.js +9 -22
- package/dist/src/evals/cli/eval.js +1 -1
- package/dist/src/internal/application/package.js +1 -1
- package/dist/src/internal/authored-definition/channel.d.ts +7 -9
- package/dist/src/internal/authored-definition/channel.js +9 -33
- package/dist/src/internal/nitro/routes/channel-dispatch.d.ts +5 -3
- package/dist/src/internal/nitro/routes/channel-dispatch.js +8 -5
- package/dist/src/internal/nitro/routes/runtime-stack.js +1 -1
- package/dist/src/internal/nitro/routes/schedule-task.d.ts +3 -4
- package/dist/src/internal/nitro/routes/schedule-task.js +5 -7
- package/dist/src/public/channels/{http.d.ts → ash.d.ts} +2 -2
- package/dist/src/public/channels/{http.js → ash.js} +2 -2
- package/dist/src/public/channels/slack/slackChannel.js +1 -1
- package/dist/src/public/definitions/channel.d.ts +1 -63
- package/dist/src/public/definitions/channel.js +0 -16
- package/dist/src/public/definitions/defineChannel.d.ts +7 -4
- package/dist/src/runtime/framework-channels/index.d.ts +1 -1
- package/dist/src/runtime/framework-channels/index.js +7 -7
- package/dist/src/runtime/resolve-channel.d.ts +9 -4
- package/dist/src/runtime/resolve-channel.js +21 -28
- package/dist/src/runtime/types.d.ts +13 -8
- package/package.json +5 -5
- package/dist/src/compiler/channel-url.d.ts +0 -5
- package/dist/src/compiler/channel-url.js +0 -14
|
@@ -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-
|
|
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-DQbfjCIS.js";import{t as m}from"./authored-module-loader-CqZSviWm.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-
|
|
1
|
+
import{D as e,b as t,t as n,v as r,y as i}from"../../chunks/paths-DQbfjCIS.js";import{d as a,f as o,g as s}from"../../chunks/types-D9Uv7nU4.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};
|
package/dist/src/cli/run.js
CHANGED
|
@@ -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-
|
|
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-CQ7AZID3.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-CQ7AZID3.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 e=await(a.buildHost??await c())(s);r.log(n(S,{message:`built output at ${e}`,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};
|
|
@@ -9,10 +9,10 @@ import type { ChannelRouteIdentityMap } from "#compiler/normalize-schedule.js";
|
|
|
9
9
|
* entry so the runtime can short-circuit channel registration without
|
|
10
10
|
* losing the source path for diagnostics.
|
|
11
11
|
*
|
|
12
|
-
*
|
|
13
|
-
* in the `routes` array becomes
|
|
14
|
-
*
|
|
15
|
-
* path comes from the route's `path` field.
|
|
12
|
+
* Authored channels are always `CompiledChannel` values (from
|
|
13
|
+
* `defineChannel`). Each route in the channel's `routes` array becomes
|
|
14
|
+
* a separate compiled channel entry. The channel name is derived from
|
|
15
|
+
* the filesystem path; the URL path comes from the route's `path` field.
|
|
16
16
|
*/
|
|
17
17
|
export declare function compileChannelDefinition(agentRoot: string, source: ChannelSourceRef): Promise<CompiledChannelEntry | readonly CompiledChannelEntry[]>;
|
|
18
18
|
/**
|
|
@@ -1,9 +1,8 @@
|
|
|
1
1
|
import { join, resolve } from "node:path";
|
|
2
2
|
import { stripLogicalPathExtension } from "#discover/filesystem.js";
|
|
3
|
-
import {
|
|
3
|
+
import { normalizeChannelDefinition } from "#internal/authored-definition/channel.js";
|
|
4
4
|
import { registerChannelModuleInCache } from "#internal/authored-module-loader.js";
|
|
5
5
|
import { isDisabledRouteSentinel } from "#public/definitions/channel.js";
|
|
6
|
-
import { deriveChannelUrlPath } from "#compiler/channel-url.js";
|
|
7
6
|
import { loadModuleBackedDefinition } from "#compiler/normalize-helpers.js";
|
|
8
7
|
/**
|
|
9
8
|
* Compiles one authored channel module into the normalized channel
|
|
@@ -13,10 +12,10 @@ import { loadModuleBackedDefinition } from "#compiler/normalize-helpers.js";
|
|
|
13
12
|
* entry so the runtime can short-circuit channel registration without
|
|
14
13
|
* losing the source path for diagnostics.
|
|
15
14
|
*
|
|
16
|
-
*
|
|
17
|
-
* in the `routes` array becomes
|
|
18
|
-
*
|
|
19
|
-
* path comes from the route's `path` field.
|
|
15
|
+
* Authored channels are always `CompiledChannel` values (from
|
|
16
|
+
* `defineChannel`). Each route in the channel's `routes` array becomes
|
|
17
|
+
* a separate compiled channel entry. The channel name is derived from
|
|
18
|
+
* the filesystem path; the URL path comes from the route's `path` field.
|
|
20
19
|
*/
|
|
21
20
|
export async function compileChannelDefinition(agentRoot, source) {
|
|
22
21
|
const rawValue = await loadModuleBackedDefinition({
|
|
@@ -33,28 +32,16 @@ export async function compileChannelDefinition(agentRoot, source) {
|
|
|
33
32
|
};
|
|
34
33
|
}
|
|
35
34
|
const definition = normalizeChannelDefinition(rawValue, `Expected the channel export "${source.exportName ?? "default"}" from "${source.logicalPath}" to match the public Ash shape.`);
|
|
36
|
-
|
|
37
|
-
return definition.routes.map((route) => ({
|
|
38
|
-
kind: "channel",
|
|
39
|
-
name: channelName,
|
|
40
|
-
logicalPath: source.logicalPath,
|
|
41
|
-
method: route.method.toUpperCase(),
|
|
42
|
-
urlPath: route.path,
|
|
43
|
-
sourceId: source.sourceId,
|
|
44
|
-
sourceKind: "module",
|
|
45
|
-
exportName: source.exportName,
|
|
46
|
-
}));
|
|
47
|
-
}
|
|
48
|
-
return {
|
|
35
|
+
return definition.routes.map((route) => ({
|
|
49
36
|
kind: "channel",
|
|
50
37
|
name: channelName,
|
|
51
38
|
logicalPath: source.logicalPath,
|
|
52
|
-
method:
|
|
53
|
-
urlPath:
|
|
39
|
+
method: route.method.toUpperCase(),
|
|
40
|
+
urlPath: route.path,
|
|
54
41
|
sourceId: source.sourceId,
|
|
55
42
|
sourceKind: "module",
|
|
56
43
|
exportName: source.exportName,
|
|
57
|
-
};
|
|
44
|
+
}));
|
|
58
45
|
}
|
|
59
46
|
/**
|
|
60
47
|
* Loads all channel modules and builds a map from route object identity
|
|
@@ -1 +1 @@
|
|
|
1
|
-
import{n as e}from"../../chunks/paths-
|
|
1
|
+
import{n as e}from"../../chunks/paths-DQbfjCIS.js";import{loadDevelopmentEnvironmentFiles as t}from"../../cli/dev/environment.js";import{a as n,n as r,t as i}from"../../chunks/client-DBMG7iuf.js";import{n as a}from"../../chunks/host-CQ7AZID3.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.7.
|
|
9
|
+
const BUNDLED_FALLBACK_PACKAGE_VERSION = "0.7.3";
|
|
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",
|
|
@@ -1,4 +1,3 @@
|
|
|
1
|
-
import type { Route } from "#public/definitions/channel.js";
|
|
2
1
|
import { type CompiledChannel } from "#channel/compiled-channel.js";
|
|
3
2
|
/**
|
|
4
3
|
* Type guard: returns whether `value` is a {@link CompiledChannel} produced
|
|
@@ -9,15 +8,14 @@ export declare function isCompiledChannel(value: unknown): value is CompiledChan
|
|
|
9
8
|
* Normalizes one authored channel definition into the canonical internal
|
|
10
9
|
* shape consumed by the compiler.
|
|
11
10
|
*
|
|
12
|
-
*
|
|
13
|
-
*
|
|
14
|
-
*
|
|
15
|
-
*
|
|
16
|
-
*
|
|
17
|
-
*
|
|
18
|
-
* are validated and returned as Route.
|
|
11
|
+
* Authored channels must go through {@link defineChannel} (or a wrapper
|
|
12
|
+
* like `slackChannel` / `ashChannel`) and therefore must be
|
|
13
|
+
* {@link CompiledChannel} values. The legacy plain-`{ fetch, receive? }`
|
|
14
|
+
* Route shape is no longer supported — drop a clear error for it so
|
|
15
|
+
* users on old patterns get a useful migration hint instead of a silent
|
|
16
|
+
* runtime crash deeper in dispatch.
|
|
19
17
|
*
|
|
20
18
|
* Disable sentinels are handled by the compiler before this function is
|
|
21
19
|
* called.
|
|
22
20
|
*/
|
|
23
|
-
export declare function normalizeChannelDefinition(value: unknown, message: string):
|
|
21
|
+
export declare function normalizeChannelDefinition(value: unknown, message: string): CompiledChannel;
|
|
@@ -1,12 +1,4 @@
|
|
|
1
1
|
import { CHANNEL_SENTINEL } from "#channel/compiled-channel.js";
|
|
2
|
-
import { expectFunction, expectObjectRecord, expectOnlyKnownKeys, } from "#internal/authored-module.js";
|
|
3
|
-
const CHANNEL_METHODS = new Set([
|
|
4
|
-
"GET",
|
|
5
|
-
"POST",
|
|
6
|
-
"PUT",
|
|
7
|
-
"DELETE",
|
|
8
|
-
"PATCH",
|
|
9
|
-
]);
|
|
10
2
|
/**
|
|
11
3
|
* Type guard: returns whether `value` is a {@link CompiledChannel} produced
|
|
12
4
|
* by {@link defineChannel}.
|
|
@@ -20,35 +12,19 @@ export function isCompiledChannel(value) {
|
|
|
20
12
|
* Normalizes one authored channel definition into the canonical internal
|
|
21
13
|
* shape consumed by the compiler.
|
|
22
14
|
*
|
|
23
|
-
*
|
|
24
|
-
*
|
|
25
|
-
*
|
|
26
|
-
*
|
|
27
|
-
*
|
|
28
|
-
*
|
|
29
|
-
* are validated and returned as Route.
|
|
15
|
+
* Authored channels must go through {@link defineChannel} (or a wrapper
|
|
16
|
+
* like `slackChannel` / `ashChannel`) and therefore must be
|
|
17
|
+
* {@link CompiledChannel} values. The legacy plain-`{ fetch, receive? }`
|
|
18
|
+
* Route shape is no longer supported — drop a clear error for it so
|
|
19
|
+
* users on old patterns get a useful migration hint instead of a silent
|
|
20
|
+
* runtime crash deeper in dispatch.
|
|
30
21
|
*
|
|
31
22
|
* Disable sentinels are handled by the compiler before this function is
|
|
32
23
|
* called.
|
|
33
24
|
*/
|
|
34
25
|
export function normalizeChannelDefinition(value, message) {
|
|
35
|
-
if (isCompiledChannel(value)) {
|
|
36
|
-
|
|
37
|
-
}
|
|
38
|
-
const record = expectObjectRecord(value, message);
|
|
39
|
-
expectOnlyKnownKeys(record, ["adapter", "fetch", "method", "receive"], message);
|
|
40
|
-
const fetchHandler = expectFunction(record.fetch, message);
|
|
41
|
-
let method;
|
|
42
|
-
if (record.method !== undefined) {
|
|
43
|
-
if (typeof record.method !== "string" || !CHANNEL_METHODS.has(record.method)) {
|
|
44
|
-
throw new Error(message);
|
|
45
|
-
}
|
|
46
|
-
method = record.method;
|
|
47
|
-
}
|
|
48
|
-
const adapter = record.adapter;
|
|
49
|
-
const receive = typeof record.receive === "function" ? record.receive : undefined;
|
|
50
|
-
if (method === undefined) {
|
|
51
|
-
return { adapter, fetch: fetchHandler, receive };
|
|
26
|
+
if (!isCompiledChannel(value)) {
|
|
27
|
+
throw new Error(`${message} Use \`defineChannel({ routes, ... })\` (or a wrapper like \`slackChannel\` / \`ashChannel\`) — bare \`{ fetch, receive? }\` channel objects are no longer supported.`);
|
|
52
28
|
}
|
|
53
|
-
return
|
|
29
|
+
return value;
|
|
54
30
|
}
|
|
@@ -11,8 +11,10 @@ import type { NitroArtifactsConfig } from "#internal/nitro/routes/runtime-artifa
|
|
|
11
11
|
* Nitro forwards that work to `event.waitUntil()` so webhook
|
|
12
12
|
* acknowledgements can return immediately.
|
|
13
13
|
*
|
|
14
|
-
*
|
|
15
|
-
*
|
|
16
|
-
*
|
|
14
|
+
* Two dispatch shapes: authored channels (`defineChannel` and its
|
|
15
|
+
* wrappers) carry a `handler` field and receive `RouteHandlerArgs` with
|
|
16
|
+
* `send`, `getSession`, etc. Framework-internal channels (the
|
|
17
|
+
* connection callback route) build `ResolvedChannelDefinition` directly
|
|
18
|
+
* with just `fetch` and receive a `RouteContext` carrying `agent`.
|
|
17
19
|
*/
|
|
18
20
|
export declare function dispatchChannelRequest(event: H3Event, routeKey: string, config: NitroArtifactsConfig): Promise<Response>;
|
|
@@ -12,9 +12,11 @@ import { resolveNitroChannelRuntimeBundle } from "#internal/nitro/routes/runtime
|
|
|
12
12
|
* Nitro forwards that work to `event.waitUntil()` so webhook
|
|
13
13
|
* acknowledgements can return immediately.
|
|
14
14
|
*
|
|
15
|
-
*
|
|
16
|
-
*
|
|
17
|
-
*
|
|
15
|
+
* Two dispatch shapes: authored channels (`defineChannel` and its
|
|
16
|
+
* wrappers) carry a `handler` field and receive `RouteHandlerArgs` with
|
|
17
|
+
* `send`, `getSession`, etc. Framework-internal channels (the
|
|
18
|
+
* connection callback route) build `ResolvedChannelDefinition` directly
|
|
19
|
+
* with just `fetch` and receive a `RouteContext` carrying `agent`.
|
|
18
20
|
*/
|
|
19
21
|
export async function dispatchChannelRequest(event, routeKey, config) {
|
|
20
22
|
const bundle = await resolveNitroChannelRuntimeBundle(config);
|
|
@@ -34,7 +36,7 @@ export async function dispatchChannelRequest(event, routeKey, config) {
|
|
|
34
36
|
};
|
|
35
37
|
let response;
|
|
36
38
|
if (matchedChannel.handler) {
|
|
37
|
-
//
|
|
39
|
+
// Authored CompiledChannel route — build RouteHandlerArgs.
|
|
38
40
|
const adapter = matchedChannel.adapter ?? { kind: "channel" };
|
|
39
41
|
const send = createSendFn(bundle.runtime, adapter, matchedChannel.name);
|
|
40
42
|
const getSession = createGetSessionFn(bundle.runtime);
|
|
@@ -48,7 +50,8 @@ export async function dispatchChannelRequest(event, routeKey, config) {
|
|
|
48
50
|
response = await matchedChannel.handler(event.req, args);
|
|
49
51
|
}
|
|
50
52
|
else {
|
|
51
|
-
//
|
|
53
|
+
// Framework-internal fetch-only channel (e.g. the connection
|
|
54
|
+
// callback route). Build a RouteContext with the agent handle.
|
|
52
55
|
const ctx = {
|
|
53
56
|
agent: bundle.runtime,
|
|
54
57
|
waitUntil,
|
|
@@ -42,7 +42,7 @@ export function resolveNitroScheduleDispatcher(config) {
|
|
|
42
42
|
if (!channel) {
|
|
43
43
|
throw new Error(`Channel "${channelName}" not found in agent/channels/.`);
|
|
44
44
|
}
|
|
45
|
-
return { receive: channel.receive };
|
|
45
|
+
return { receive: channel.receive, adapter: channel.adapter };
|
|
46
46
|
},
|
|
47
47
|
});
|
|
48
48
|
}
|
|
@@ -2,12 +2,11 @@ import type { NitroArtifactsConfig } from "#internal/nitro/routes/runtime-artifa
|
|
|
2
2
|
/**
|
|
3
3
|
* Dispatches one Ash authored schedule via the execution engine.
|
|
4
4
|
*
|
|
5
|
-
*
|
|
6
|
-
*
|
|
7
|
-
*
|
|
5
|
+
* Fire-and-forget: the workflow runtime owns terminal completion and
|
|
6
|
+
* its own failure observability. The task return value reports only
|
|
7
|
+
* that the session was dispatched.
|
|
8
8
|
*/
|
|
9
9
|
export declare function dispatchScheduleTask(taskName: string, config: NitroArtifactsConfig): Promise<{
|
|
10
10
|
scheduleId: string;
|
|
11
11
|
sessionId: string;
|
|
12
|
-
status: string;
|
|
13
12
|
}>;
|
|
@@ -4,9 +4,9 @@ import { resolveNitroScheduleDispatcher } from "#internal/nitro/routes/runtime-s
|
|
|
4
4
|
/**
|
|
5
5
|
* Dispatches one Ash authored schedule via the execution engine.
|
|
6
6
|
*
|
|
7
|
-
*
|
|
8
|
-
*
|
|
9
|
-
*
|
|
7
|
+
* Fire-and-forget: the workflow runtime owns terminal completion and
|
|
8
|
+
* its own failure observability. The task return value reports only
|
|
9
|
+
* that the session was dispatched.
|
|
10
10
|
*/
|
|
11
11
|
export async function dispatchScheduleTask(taskName, config) {
|
|
12
12
|
const compiledArtifactsSource = resolveNitroCompiledArtifactsSource(config);
|
|
@@ -19,11 +19,9 @@ export async function dispatchScheduleTask(taskName, config) {
|
|
|
19
19
|
markdown: schedule.markdown,
|
|
20
20
|
scheduleId: schedule.name,
|
|
21
21
|
};
|
|
22
|
-
const
|
|
23
|
-
const result = await handle.result;
|
|
22
|
+
const session = await dispatcher.trigger(triggerInput);
|
|
24
23
|
return {
|
|
25
24
|
scheduleId: schedule.name,
|
|
26
|
-
sessionId:
|
|
27
|
-
status: result.status,
|
|
25
|
+
sessionId: session.id,
|
|
28
26
|
};
|
|
29
27
|
}
|
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
import { type AuthFn } from "#public/channels/auth.js";
|
|
2
2
|
import { type UploadPolicy } from "#public/channels/upload-policy.js";
|
|
3
3
|
import { type Channel } from "#public/definitions/defineChannel.js";
|
|
4
|
-
export interface
|
|
4
|
+
export interface AshChannelInput {
|
|
5
5
|
readonly auth: AuthFn<Request>;
|
|
6
6
|
readonly uploadPolicy?: Partial<UploadPolicy>;
|
|
7
7
|
}
|
|
8
|
-
export declare function
|
|
8
|
+
export declare function ashChannel(input: AshChannelInput): Channel;
|
|
@@ -3,7 +3,7 @@ import { isInputResponse } from "#runtime/input/types.js";
|
|
|
3
3
|
import { createUnauthorizedResponse } from "#public/channels/auth.js";
|
|
4
4
|
import { collectUploadPolicyViolations, formatUploadPolicyViolation, mergeUploadPolicy, } from "#public/channels/upload-policy.js";
|
|
5
5
|
import { defineChannel, POST, GET } from "#public/definitions/defineChannel.js";
|
|
6
|
-
export function
|
|
6
|
+
export function ashChannel(input) {
|
|
7
7
|
const uploadPolicy = mergeUploadPolicy(input.uploadPolicy);
|
|
8
8
|
return defineChannel({
|
|
9
9
|
routes: [
|
|
@@ -28,7 +28,7 @@ export function httpChannel(input) {
|
|
|
28
28
|
const policyRejection = checkUploadPolicy(body, uploadPolicy);
|
|
29
29
|
if (policyRejection !== null)
|
|
30
30
|
return policyRejection;
|
|
31
|
-
const token = `
|
|
31
|
+
const token = `ash:${crypto.randomUUID()}`;
|
|
32
32
|
const messageText = typeof body.message === "string"
|
|
33
33
|
? body.message
|
|
34
34
|
: body.message
|
|
@@ -1,5 +1,4 @@
|
|
|
1
|
-
import type {
|
|
2
|
-
import type { DeliverInput, GetEventStreamOptions, RunHandle, RunInput, SessionAuthContext } from "#channel/types.js";
|
|
1
|
+
import type { DeliverInput, GetEventStreamOptions, RunHandle, RunInput } from "#channel/types.js";
|
|
3
2
|
import type { HandleMessageStreamEvent } from "#protocol/message.js";
|
|
4
3
|
export type { GetEventStreamOptions } from "#channel/types.js";
|
|
5
4
|
/**
|
|
@@ -94,59 +93,6 @@ export interface Agent {
|
|
|
94
93
|
*/
|
|
95
94
|
getEventStream(sessionId: string, options?: GetEventStreamOptions): Promise<ReadableStream<HandleMessageStreamEvent>>;
|
|
96
95
|
}
|
|
97
|
-
/**
|
|
98
|
-
* Input for {@link Route.receive} — the universal entry point for
|
|
99
|
-
* inbound messages to a channel.
|
|
100
|
-
*/
|
|
101
|
-
export interface ReceiveInput {
|
|
102
|
-
readonly message: string;
|
|
103
|
-
readonly args: Readonly<Record<string, unknown>>;
|
|
104
|
-
readonly auth: SessionAuthContext | null;
|
|
105
|
-
}
|
|
106
|
-
/**
|
|
107
|
-
* Context passed to {@link Route.receive}.
|
|
108
|
-
*/
|
|
109
|
-
export interface ReceiveContext {
|
|
110
|
-
readonly agent: Agent;
|
|
111
|
-
readonly waitUntil: (task: Promise<unknown>) => void;
|
|
112
|
-
}
|
|
113
|
-
/**
|
|
114
|
-
* Public authored route definition. A route is a Web-standard
|
|
115
|
-
* `(Request, ctx) => Response` handler whose URL path is derived from the
|
|
116
|
-
* file's location under `agent/channels/`.
|
|
117
|
-
*
|
|
118
|
-
* Filename is identity. `agent/channels/slack.ts` mounts at `POST /slack`.
|
|
119
|
-
* Nested directories and Nitro `[name]` path parameters are preserved.
|
|
120
|
-
*/
|
|
121
|
-
export interface Route {
|
|
122
|
-
/**
|
|
123
|
-
* HTTP method this route handles. Optional — defaults to `"POST"`.
|
|
124
|
-
*/
|
|
125
|
-
readonly method?: ChannelMethod;
|
|
126
|
-
/**
|
|
127
|
-
* Optional adapter that this route attaches to sessions it starts.
|
|
128
|
-
*
|
|
129
|
-
* Declared here so the runtime can register the adapter's `kind` in
|
|
130
|
-
* the adapter registry and rehydrate it after workflow step boundaries.
|
|
131
|
-
* Routes that don't start sessions (e.g. stream readers) omit this.
|
|
132
|
-
*/
|
|
133
|
-
readonly adapter?: ChannelAdapter<any>;
|
|
134
|
-
/**
|
|
135
|
-
* The request handler. Receives the inbound `Request` and a
|
|
136
|
-
* {@link RouteContext} carrying the agent handle and host helpers.
|
|
137
|
-
*/
|
|
138
|
-
fetch(request: Request, ctx: RouteContext): Promise<Response>;
|
|
139
|
-
/**
|
|
140
|
-
* Universal entry point for inbound messages. Handles both new
|
|
141
|
-
* sessions (start) and continuations (deliver to existing session).
|
|
142
|
-
* Called by `fetch` after HTTP parsing and by the schedule dispatcher
|
|
143
|
-
* directly.
|
|
144
|
-
*
|
|
145
|
-
* Returns `RunHandle` for new sessions, `void` for deliveries to
|
|
146
|
-
* existing sessions.
|
|
147
|
-
*/
|
|
148
|
-
receive?(input: ReceiveInput, ctx: ReceiveContext): Promise<RunHandle | void>;
|
|
149
|
-
}
|
|
150
96
|
/**
|
|
151
97
|
* Marker discriminator written into every {@link DisabledRouteSentinel}.
|
|
152
98
|
*/
|
|
@@ -171,11 +117,3 @@ export declare function disableRoute(): DisabledRouteSentinel;
|
|
|
171
117
|
* produced by {@link disableRoute}.
|
|
172
118
|
*/
|
|
173
119
|
export declare function isDisabledRouteSentinel(value: unknown): value is DisabledRouteSentinel;
|
|
174
|
-
/**
|
|
175
|
-
* Type guard: returns whether `value` looks like a {@link Route} or a
|
|
176
|
-
* `CompiledChannel` produced by `defineChannel()`.
|
|
177
|
-
*
|
|
178
|
-
* Used by the compiler to distinguish authored channel exports from
|
|
179
|
-
* `DisabledRouteSentinel` exports.
|
|
180
|
-
*/
|
|
181
|
-
export declare function isRouteDefinition(value: unknown): boolean;
|
|
@@ -22,19 +22,3 @@ export function isDisabledRouteSentinel(value) {
|
|
|
22
22
|
value !== null &&
|
|
23
23
|
value.kind === DISABLED_ROUTE_SENTINEL_KIND);
|
|
24
24
|
}
|
|
25
|
-
/**
|
|
26
|
-
* Type guard: returns whether `value` looks like a {@link Route} or a
|
|
27
|
-
* `CompiledChannel` produced by `defineChannel()`.
|
|
28
|
-
*
|
|
29
|
-
* Used by the compiler to distinguish authored channel exports from
|
|
30
|
-
* `DisabledRouteSentinel` exports.
|
|
31
|
-
*/
|
|
32
|
-
export function isRouteDefinition(value) {
|
|
33
|
-
if (typeof value !== "object" || value === null)
|
|
34
|
-
return false;
|
|
35
|
-
// New CompiledChannel shape from defineChannel()
|
|
36
|
-
if (value.__kind === "ash:channel")
|
|
37
|
-
return true;
|
|
38
|
-
// Old Route shape with fetch handler
|
|
39
|
-
return typeof value.fetch === "function";
|
|
40
|
-
}
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import type { AttachmentResolver } from "#channel/adapter.js";
|
|
2
2
|
import { CHANNEL_SENTINEL } from "#channel/compiled-channel.js";
|
|
3
|
+
import type { SessionAuthContext } from "#channel/types.js";
|
|
3
4
|
import type { HandleMessageStreamEvent } from "#protocol/message.js";
|
|
4
5
|
import type { RouteDefinition, SendFn } from "#channel/routes.js";
|
|
5
6
|
import type { Session, SessionHandle } from "#channel/session.js";
|
|
@@ -34,8 +35,9 @@ export interface ChannelConfig<TState = undefined, TCtx = void> {
|
|
|
34
35
|
context?(state: NonNullable<TState>): TCtx;
|
|
35
36
|
readonly routes: readonly RouteDefinition<TState>[];
|
|
36
37
|
receive?(input: {
|
|
37
|
-
message: string;
|
|
38
|
-
args: Readonly<Record<string, unknown>>;
|
|
38
|
+
readonly message: string;
|
|
39
|
+
readonly args: Readonly<Record<string, unknown>>;
|
|
40
|
+
readonly auth: SessionAuthContext | null;
|
|
39
41
|
}, args: {
|
|
40
42
|
send: SendFn<TState>;
|
|
41
43
|
}): Promise<Session>;
|
|
@@ -55,8 +57,9 @@ export interface Channel<TState = undefined> {
|
|
|
55
57
|
path: string;
|
|
56
58
|
}[];
|
|
57
59
|
readonly receive?: (input: {
|
|
58
|
-
message: string;
|
|
59
|
-
args: Readonly<Record<string, unknown>>;
|
|
60
|
+
readonly message: string;
|
|
61
|
+
readonly args: Readonly<Record<string, unknown>>;
|
|
62
|
+
readonly auth: SessionAuthContext | null;
|
|
60
63
|
}, args: {
|
|
61
64
|
send: SendFn<TState>;
|
|
62
65
|
}) => Promise<Session>;
|
|
@@ -1,4 +1,4 @@
|
|
|
1
1
|
import type { ResolvedChannelDefinition } from "#runtime/types.js";
|
|
2
|
-
export declare const
|
|
2
|
+
export declare const ASH_CHANNEL_NAME = "ash";
|
|
3
3
|
export declare function getFrameworkChannelDefinitions(): readonly ResolvedChannelDefinition[];
|
|
4
4
|
export declare function getAllFrameworkChannelNames(): ReadonlySet<string>;
|
|
@@ -1,14 +1,14 @@
|
|
|
1
1
|
import { none, vercelOidc } from "#public/channels/auth.js";
|
|
2
|
-
import {
|
|
2
|
+
import { ashChannel } from "#public/channels/ash.js";
|
|
3
3
|
import { getConnectionCallbackChannelDefinitions, getConnectionCallbackChannelNames, } from "#runtime/connections/callback-route.js";
|
|
4
|
-
export const
|
|
4
|
+
export const ASH_CHANNEL_NAME = "ash";
|
|
5
5
|
export function getFrameworkChannelDefinitions() {
|
|
6
|
-
const auth =
|
|
7
|
-
const compiled =
|
|
6
|
+
const auth = resolveFrameworkAshAuth();
|
|
7
|
+
const compiled = ashChannel({ auth });
|
|
8
8
|
const result = [];
|
|
9
9
|
for (const route of compiled.routes) {
|
|
10
10
|
result.push({
|
|
11
|
-
name:
|
|
11
|
+
name: ASH_CHANNEL_NAME,
|
|
12
12
|
method: route.method.toUpperCase(),
|
|
13
13
|
urlPath: route.path,
|
|
14
14
|
fetch: async (req, ctx) => route.handler(req, ctx),
|
|
@@ -23,9 +23,9 @@ export function getFrameworkChannelDefinitions() {
|
|
|
23
23
|
return result;
|
|
24
24
|
}
|
|
25
25
|
export function getAllFrameworkChannelNames() {
|
|
26
|
-
return new Set([
|
|
26
|
+
return new Set([ASH_CHANNEL_NAME, ...getConnectionCallbackChannelNames()]);
|
|
27
27
|
}
|
|
28
|
-
function
|
|
28
|
+
function resolveFrameworkAshAuth() {
|
|
29
29
|
if (process.env.VERCEL) {
|
|
30
30
|
return vercelOidc();
|
|
31
31
|
}
|
|
@@ -3,10 +3,15 @@ import type { CompiledModuleMap } from "#compiler/module-map.js";
|
|
|
3
3
|
import type { ResolvedChannelDefinition } from "#runtime/types.js";
|
|
4
4
|
/**
|
|
5
5
|
* Resolves one compiled channel entry into a runtime-owned definition
|
|
6
|
-
* with a live `
|
|
6
|
+
* with a live `handler` (the per-route handler authored via `POST` /
|
|
7
|
+
* `GET` / etc. inside `defineChannel`) and the channel's `receive` hook
|
|
8
|
+
* if the author declared one.
|
|
7
9
|
*
|
|
8
|
-
*
|
|
9
|
-
*
|
|
10
|
-
*
|
|
10
|
+
* Every authored channel is a `CompiledChannel` from `defineChannel` —
|
|
11
|
+
* the bare `{ fetch, receive? }` Route shape is rejected by
|
|
12
|
+
* {@link normalizeChannelDefinition}. Framework-internal channels
|
|
13
|
+
* (the connection callback route, the `ash` session channel) build
|
|
14
|
+
* `ResolvedChannelDefinition` values directly and do not flow through
|
|
15
|
+
* this resolver.
|
|
11
16
|
*/
|
|
12
17
|
export declare function resolveChannelDefinition(definition: CompiledChannelDefinition, moduleMap: CompiledModuleMap, nodeId: string | undefined): Promise<ResolvedChannelDefinition>;
|