Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions .changeset/cli-analytics-list-reads.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
'@workflow/cli': patch
---

`inspect` list views (runs, steps, events, sleeps) now read from the optional `world.analytics` namespace when the backend provides one, falling back to the runtime storage APIs otherwise. Hook listing stays on the runtime storage APIs (the analytics rows omit `ownerId` and the hook token). Payload/detail views are unchanged. The `--withData` flag is deprecated for list views; use `workflow inspect <resource> <id>` to view payloads for a single resource.
5 changes: 5 additions & 0 deletions .changeset/cli-generic-world-backends.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
'@workflow/cli': patch
---

Ensure the CLI can resolve community world packages if not statically injected. Fixes "Unsupported workflow backend" error.
6 changes: 6 additions & 0 deletions .changeset/fix-sveltekit-filename-chunk-patch.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
---
"@workflow/sveltekit": patch
"@workflow/builders": patch
---

Fix SvelteKit production server crash at boot if a world package pulls cosmiconfig into the server bundle.
5 changes: 5 additions & 0 deletions .changeset/hook-token-claim-release.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
'@workflow/world-local': patch
---

Fix hook token claims of disposed hooks and finished runs not being released to the next claimant
5 changes: 5 additions & 0 deletions .changeset/hook-token-reuse-after-dispose.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
'@workflow/core': patch
---

Fix `createHook()` conflicting with the run's own disposed hook when a token is reused after `dispose()` within the same run
9 changes: 9 additions & 0 deletions .changeset/web-analytics-list-reads.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
---
'@workflow/web': patch
---

The runs, steps, and events observability list views now read from the
metadata-only `world.analytics` namespace when the configured backend provides
one, and fall back to the runtime storage APIs otherwise. Event payloads are
still loaded lazily per event on the runtime path. Hooks listing, detail views,
payload resolution, streams, and mutations are unchanged.
5 changes: 5 additions & 0 deletions .changeset/web-hook-token-lazy.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
'@workflow/web': patch
---

The hooks list now reads from the metadata-only `world.analytics` namespace when the backend provides one (falling back to the runtime storage APIs otherwise). A hook's secret token is no longer included in list rows — it is fetched one hook at a time via `world.hooks.get` only when the user copies the token or resumes the hook.
5 changes: 5 additions & 0 deletions .changeset/web-local-world-static-injection.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
'@workflow/web': patch
---

Fix `workflow web` for local and postgres backends after static world-target injection. The web server now constructs the local world directly and resolves other world packages from the inspected project, instead of calling the `createWorld()` static-injection stub (which throws when no build plugin aliased it).
1 change: 1 addition & 0 deletions packages/builders/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,7 @@ export {
} from './node-compat-banner.js';
export { createNodeModuleErrorPlugin } from './node-module-esbuild-plugin.js';
export { WORKFLOW_OPTIONAL_PG_NATIVE_ALIAS } from './optional-pg-native-alias.js';
export { WORKFLOW_OPTIONAL_TYPESCRIPT_ALIAS } from './optional-typescript-alias.js';
export {
createPseudoPackagePlugin,
PSEUDO_PACKAGES,
Expand Down
5 changes: 5 additions & 0 deletions packages/builders/src/optional-typescript-alias.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
import { fileURLToPath } from 'node:url';

export const WORKFLOW_OPTIONAL_TYPESCRIPT_ALIAS = fileURLToPath(
new URL('./optional-typescript.js', import.meta.url)
);
9 changes: 9 additions & 0 deletions packages/builders/src/optional-typescript.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
// Stub aliased in place of the `typescript` package in framework server
// bundles. It is only reachable through cosmiconfig's TS-config loader
// (via world packages -> graphile-worker), where `require('typescript')`
// is lazy and never fires at runtime — but bundling converts it into an
// eager top-level evaluation, pulling the entire compiler into the server
// output and executing it at boot.
const typescript = {};

export default typescript;
41 changes: 33 additions & 8 deletions packages/cli/src/commands/cancel.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,10 +2,15 @@ import readline from 'node:readline';
import { Args, Flags } from '@oclif/core';
import { cancelRun } from '@workflow/core/runtime';
import { parseWorkflowName } from '@workflow/utils/parse-name';
import type { WorkflowRun } from '@workflow/world';
import chalk from 'chalk';
import Table from 'easy-table';
import { BaseCommand } from '../base.js';
import { LOGGING_CONFIG, logger } from '../lib/config/log.js';
import {
getObservabilityUpgradeRequiredMessage,
isObservabilityUpgradeRequiredError,
} from '../lib/inspect/errors.js';
import { cliFlags } from '../lib/inspect/flags.js';
import { setupCliWorld } from '../lib/inspect/setup.js';

Expand All @@ -23,7 +28,10 @@ export default class Cancel extends BaseCommand {
];

async catch(error: any) {
if (LOGGING_CONFIG.VERBOSE_MODE) {
if (isObservabilityUpgradeRequiredError(error)) {
logger.error(getObservabilityUpgradeRequiredMessage());
process.exit(1);
} else if (LOGGING_CONFIG.VERBOSE_MODE) {
console.error(error);
}
throw error;
Expand Down Expand Up @@ -99,13 +107,30 @@ export default class Cancel extends BaseCommand {
process.exit(1);
}

// Fetch matching runs
const runs = await world.runs.list({
status: flags.status as any,
workflowName: flags.workflowName,
pagination: { limit: flags.limit || 50 },
resolveData: 'none',
});
// Fetch matching runs. Only metadata is needed to display and cancel, so
// prefer the analytics read path when the backend provides one.
const status = flags.status as WorkflowRun['status'] | undefined;
const runList = world.analytics
? await world.analytics.runs.list({
status,
workflowName: flags.workflowName,
pagination: { limit: flags.limit || 50 },
})
: await world.runs.list({
status,
workflowName: flags.workflowName,
pagination: { limit: flags.limit || 50 },
resolveData: 'none',
});
const runs = {
data: runList.data.map((run) => ({
runId: run.runId,
workflowName: run.workflowName,
status: run.status,
startedAt: run.startedAt,
})),
hasMore: runList.hasMore,
};

if (runs.data.length === 0) {
logger.warn('No matching runs found.');
Expand Down
11 changes: 9 additions & 2 deletions packages/cli/src/commands/inspect.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,10 @@ import { VERCEL_403_ERROR_MESSAGE } from '@workflow/errors';
import { BaseCommand } from '../base.js';
import { LOGGING_CONFIG, logger } from '../lib/config/log.js';
import type { InspectCLIOptions } from '../lib/config/types.js';
import {
getObservabilityUpgradeRequiredMessage,
isObservabilityUpgradeRequiredError,
} from '../lib/inspect/errors.js';
import { cliFlags, urlFlag } from '../lib/inspect/flags.js';
import {
listEvents,
Expand Down Expand Up @@ -35,7 +39,9 @@ export default class Inspect extends BaseCommand {

async catch(error: any) {
// Check if this is a 403 error from the Vercel backend
if (error?.status === 403) {
if (isObservabilityUpgradeRequiredError(error)) {
logger.error(getObservabilityUpgradeRequiredMessage());
} else if (error?.status === 403) {
const message = VERCEL_403_ERROR_MESSAGE;
logger.error(message);
} else if (LOGGING_CONFIG.VERBOSE_MODE) {
Expand Down Expand Up @@ -124,7 +130,8 @@ export default class Inspect extends BaseCommand {
helpLabel: '--status',
}),
withData: Flags.boolean({
description: 'include full input/output data in list views',
description:
'include full input/output data in list views (deprecated for list views — use `inspect <resource> <id>` to view payloads)',
required: false,
char: 'd',
default: false,
Expand Down
11 changes: 9 additions & 2 deletions packages/cli/src/commands/start.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,10 @@
import { Args } from '@oclif/core';
import { BaseCommand } from '../base.js';
import { LOGGING_CONFIG } from '../lib/config/log.js';
import { LOGGING_CONFIG, logger } from '../lib/config/log.js';
import {
getObservabilityUpgradeRequiredMessage,
isObservabilityUpgradeRequiredError,
} from '../lib/inspect/errors.js';
import { cliFlags } from '../lib/inspect/flags.js';
import { startRun } from '../lib/inspect/run.js';
import { setupCliWorld } from '../lib/inspect/setup.js';
Expand All @@ -16,7 +20,10 @@ export default class Start extends BaseCommand {
];

async catch(error: any) {
if (LOGGING_CONFIG.VERBOSE_MODE) {
if (isObservabilityUpgradeRequiredError(error)) {
logger.error(getObservabilityUpgradeRequiredMessage());
process.exit(1);
} else if (LOGGING_CONFIG.VERBOSE_MODE) {
console.error(error);
}
throw error;
Expand Down
38 changes: 38 additions & 0 deletions packages/cli/src/lib/inspect/errors.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
const OBSERVABILITY_UPGRADE_REQUIRED_CODE = 'observability-upgrade-required';
const OBSERVABILITY_UPGRADE_REQUIRED_MESSAGE =
'This workflow observability data is outside your current plan window. Upgrade Observability Plus to view up to 30 days of workflow data.';

const extractErrorCode = (err: Record<string, unknown>): string | undefined => {
if (err.code && typeof err.code === 'string') {
return err.code;
}

if (err.body && typeof err.body === 'object') {
const body = err.body as Record<string, unknown>;
if (body.code && typeof body.code === 'string') {
return body.code;
}
if (body.error && typeof body.error === 'string') {
return body.error;
}
}

return undefined;
};

export const isObservabilityUpgradeRequiredError = (
error: unknown
): boolean => {
if (!error || typeof error !== 'object') {
return false;
}

const err = error as Record<string, unknown>;
return (
err.status === 402 &&
extractErrorCode(err) === OBSERVABILITY_UPGRADE_REQUIRED_CODE
);
};

export const getObservabilityUpgradeRequiredMessage = (): string =>
OBSERVABILITY_UPGRADE_REQUIRED_MESSAGE;
Loading
Loading