Replay\Cancel a run (v3) (#1006)
* WIP on replaying a task from the run page * Don’t pass the existing runs idempotency key, it will cause the replay to always return the original run * Replay from the run list * Don’t use fetchers in the replay/cancel dialogs * API endpoint for replaying a run * REST API docs (mostly coming soon) but added replay run * replayRun function added to the SDK * Cancel run added to the SDK * v3-catalog file to test canceling and replaying * Changed the SDK to be runs.replay and runs.cancel * Removed comment * Latest lockfile
This commit is contained in:
@@ -0,0 +1,6 @@
|
|||||||
|
---
|
||||||
|
"@trigger.dev/sdk": patch
|
||||||
|
"@trigger.dev/core": patch
|
||||||
|
---
|
||||||
|
|
||||||
|
Added replayRun function to the SDK
|
||||||
@@ -0,0 +1,6 @@
|
|||||||
|
---
|
||||||
|
"@trigger.dev/sdk": patch
|
||||||
|
"@trigger.dev/core": patch
|
||||||
|
---
|
||||||
|
|
||||||
|
Added cancelRun to the SDK
|
||||||
@@ -1,5 +1,5 @@
|
|||||||
import { StopCircleIcon } from "@heroicons/react/20/solid";
|
import { StopCircleIcon } from "@heroicons/react/20/solid";
|
||||||
import { useFetcher } from "@remix-run/react";
|
import { Form, useFetcher, useNavigation } from "@remix-run/react";
|
||||||
import { Button } from "~/components/primitives/Buttons";
|
import { Button } from "~/components/primitives/Buttons";
|
||||||
import {
|
import {
|
||||||
DialogContent,
|
DialogContent,
|
||||||
@@ -14,29 +14,32 @@ type CancelRunDialogProps = {
|
|||||||
};
|
};
|
||||||
|
|
||||||
export function CancelRunDialog({ runFriendlyId, redirectPath }: CancelRunDialogProps) {
|
export function CancelRunDialog({ runFriendlyId, redirectPath }: CancelRunDialogProps) {
|
||||||
const cancelFetcher = useFetcher();
|
const navigation = useNavigation();
|
||||||
|
|
||||||
|
const formAction = `/resources/taskruns/${runFriendlyId}/cancel`;
|
||||||
|
const isLoading = navigation.formAction === formAction;
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<DialogContent>
|
<DialogContent key="cancel">
|
||||||
<DialogHeader>Cancel this run?</DialogHeader>
|
<DialogHeader>Cancel this run?</DialogHeader>
|
||||||
<DialogDescription>
|
<DialogDescription>
|
||||||
Canceling a run will stop execution. If you want to run this later you will have to replay
|
Canceling a run will stop execution. If you want to run this later you will have to replay
|
||||||
the entire run with the original payload.
|
the entire run with the original payload.
|
||||||
</DialogDescription>
|
</DialogDescription>
|
||||||
<DialogFooter>
|
<DialogFooter>
|
||||||
<cancelFetcher.Form action={`/resources/taskruns/${runFriendlyId}/cancel`} method="post">
|
<Form action={`/resources/taskruns/${runFriendlyId}/cancel`} method="post">
|
||||||
<Button
|
<Button
|
||||||
type="submit"
|
type="submit"
|
||||||
name="redirectUrl"
|
name="redirectUrl"
|
||||||
value={redirectPath}
|
value={redirectPath}
|
||||||
variant="danger/small"
|
variant="danger/small"
|
||||||
LeadingIcon={cancelFetcher.state === "idle" ? StopCircleIcon : "spinner-white"}
|
LeadingIcon={isLoading ? "spinner-white" : StopCircleIcon}
|
||||||
disabled={cancelFetcher.state !== "idle"}
|
disabled={isLoading}
|
||||||
shortcut={{ modifiers: ["meta"], key: "enter" }}
|
shortcut={{ modifiers: ["meta"], key: "enter" }}
|
||||||
>
|
>
|
||||||
{cancelFetcher.state === "idle" ? "Cancel run" : "Canceling..."}
|
{isLoading ? "Canceling..." : "Cancel run"}
|
||||||
</Button>
|
</Button>
|
||||||
</cancelFetcher.Form>
|
</Form>
|
||||||
</DialogFooter>
|
</DialogFooter>
|
||||||
</DialogContent>
|
</DialogContent>
|
||||||
);
|
);
|
||||||
|
|||||||
@@ -0,0 +1,44 @@
|
|||||||
|
import { ArrowPathIcon } from "@heroicons/react/20/solid";
|
||||||
|
import { Form, useFetcher, useNavigation } from "@remix-run/react";
|
||||||
|
import { Button } from "~/components/primitives/Buttons";
|
||||||
|
import {
|
||||||
|
DialogContent,
|
||||||
|
DialogDescription,
|
||||||
|
DialogFooter,
|
||||||
|
DialogHeader,
|
||||||
|
} from "~/components/primitives/Dialog";
|
||||||
|
|
||||||
|
type ReplayRunDialogProps = {
|
||||||
|
runFriendlyId: string;
|
||||||
|
failedRedirect: string;
|
||||||
|
};
|
||||||
|
|
||||||
|
export function ReplayRunDialog({ runFriendlyId, failedRedirect }: ReplayRunDialogProps) {
|
||||||
|
const navigation = useNavigation();
|
||||||
|
|
||||||
|
const formAction = `/resources/taskruns/${runFriendlyId}/replay`;
|
||||||
|
const isLoading = navigation.formAction === formAction;
|
||||||
|
|
||||||
|
return (
|
||||||
|
<DialogContent key="replay">
|
||||||
|
<DialogHeader>Replay this run?</DialogHeader>
|
||||||
|
<DialogDescription>
|
||||||
|
Replaying a run will create a new run with the same payload and environment as the original.
|
||||||
|
</DialogDescription>
|
||||||
|
<DialogFooter>
|
||||||
|
<Form action={formAction} method="post">
|
||||||
|
<input type="hidden" name="failedRedirect" value={failedRedirect} />
|
||||||
|
<Button
|
||||||
|
type="submit"
|
||||||
|
variant="primary/small"
|
||||||
|
LeadingIcon={isLoading ? "spinner-white" : ArrowPathIcon}
|
||||||
|
disabled={isLoading}
|
||||||
|
shortcut={{ modifiers: ["meta"], key: "enter" }}
|
||||||
|
>
|
||||||
|
{isLoading ? "Replaying..." : "Replay run"}
|
||||||
|
</Button>
|
||||||
|
</Form>
|
||||||
|
</DialogFooter>
|
||||||
|
</DialogContent>
|
||||||
|
);
|
||||||
|
}
|
||||||
@@ -24,10 +24,11 @@ import { formatDuration } from "@trigger.dev/core/v3";
|
|||||||
import { TaskRunStatusCombo } from "./TaskRunStatus";
|
import { TaskRunStatusCombo } from "./TaskRunStatus";
|
||||||
import { useEnvironments } from "~/hooks/useEnvironments";
|
import { useEnvironments } from "~/hooks/useEnvironments";
|
||||||
import { Button, LinkButton } from "~/components/primitives/Buttons";
|
import { Button, LinkButton } from "~/components/primitives/Buttons";
|
||||||
import { StopCircleIcon } from "@heroicons/react/20/solid";
|
import { ArrowPathIcon, StopCircleIcon } from "@heroicons/react/20/solid";
|
||||||
import { Dialog, DialogTrigger } from "~/components/primitives/Dialog";
|
import { Dialog, DialogTrigger } from "~/components/primitives/Dialog";
|
||||||
import { CancelRunDialog } from "./CancelRunDialog";
|
import { CancelRunDialog } from "./CancelRunDialog";
|
||||||
import { useLocation } from "@remix-run/react";
|
import { useLocation } from "@remix-run/react";
|
||||||
|
import { ReplayRunDialog } from "./ReplayRunDialog";
|
||||||
|
|
||||||
type RunsTableProps = {
|
type RunsTableProps = {
|
||||||
total: number;
|
total: number;
|
||||||
@@ -49,7 +50,6 @@ export function TaskRunsTable({
|
|||||||
}: RunsTableProps) {
|
}: RunsTableProps) {
|
||||||
const organization = useOrganization();
|
const organization = useOrganization();
|
||||||
const project = useProject();
|
const project = useProject();
|
||||||
const location = useLocation();
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Table>
|
<Table>
|
||||||
@@ -110,23 +110,7 @@ export function TaskRunsTable({
|
|||||||
<TableCell to={path}>
|
<TableCell to={path}>
|
||||||
{run.createdAt ? <DateTime date={run.createdAt} /> : "–"}
|
{run.createdAt ? <DateTime date={run.createdAt} /> : "–"}
|
||||||
</TableCell>
|
</TableCell>
|
||||||
{run.isCancellable ? (
|
<RunActionsCell run={run} path={path} />
|
||||||
<TableCellMenu isSticky>
|
|
||||||
<Dialog>
|
|
||||||
<DialogTrigger asChild>
|
|
||||||
<Button variant="small-menu-item" LeadingIcon={StopCircleIcon}>
|
|
||||||
Cancel run
|
|
||||||
</Button>
|
|
||||||
</DialogTrigger>
|
|
||||||
<CancelRunDialog
|
|
||||||
runFriendlyId={run.friendlyId}
|
|
||||||
redirectPath={`${location.pathname}${location.search}`}
|
|
||||||
/>
|
|
||||||
</Dialog>
|
|
||||||
</TableCellMenu>
|
|
||||||
) : (
|
|
||||||
<TableCell to={path}>{""}</TableCell>
|
|
||||||
)}
|
|
||||||
</TableRow>
|
</TableRow>
|
||||||
);
|
);
|
||||||
})
|
})
|
||||||
@@ -144,6 +128,43 @@ export function TaskRunsTable({
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function RunActionsCell({ run, path }: { run: RunListItem; path: string }) {
|
||||||
|
const location = useLocation();
|
||||||
|
|
||||||
|
if (!run.isCancellable && !run.isReplayable) return <TableCell to={path}>{""}</TableCell>;
|
||||||
|
|
||||||
|
return (
|
||||||
|
<TableCellMenu isSticky>
|
||||||
|
{run.isCancellable && (
|
||||||
|
<Dialog>
|
||||||
|
<DialogTrigger asChild>
|
||||||
|
<Button variant="small-menu-item" LeadingIcon={StopCircleIcon}>
|
||||||
|
Cancel run
|
||||||
|
</Button>
|
||||||
|
</DialogTrigger>
|
||||||
|
<CancelRunDialog
|
||||||
|
runFriendlyId={run.friendlyId}
|
||||||
|
redirectPath={`${location.pathname}${location.search}`}
|
||||||
|
/>
|
||||||
|
</Dialog>
|
||||||
|
)}
|
||||||
|
{run.isReplayable && (
|
||||||
|
<Dialog>
|
||||||
|
<DialogTrigger asChild>
|
||||||
|
<Button variant="small-menu-item" LeadingIcon={ArrowPathIcon}>
|
||||||
|
Replay run
|
||||||
|
</Button>
|
||||||
|
</DialogTrigger>
|
||||||
|
<ReplayRunDialog
|
||||||
|
runFriendlyId={run.friendlyId}
|
||||||
|
failedRedirect={`${location.pathname}${location.search}`}
|
||||||
|
/>
|
||||||
|
</Dialog>
|
||||||
|
)}
|
||||||
|
</TableCellMenu>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
function NoRuns({ title }: { title: string }) {
|
function NoRuns({ title }: { title: string }) {
|
||||||
return (
|
return (
|
||||||
<div className="flex items-center justify-center">
|
<div className="flex items-center justify-center">
|
||||||
|
|||||||
@@ -222,6 +222,7 @@ export class RunListPresenter {
|
|||||||
version: run.version,
|
version: run.version,
|
||||||
taskIdentifier: run.taskIdentifier,
|
taskIdentifier: run.taskIdentifier,
|
||||||
attempts: Number(run.attempts),
|
attempts: Number(run.attempts),
|
||||||
|
isReplayable: true,
|
||||||
isCancellable: CANCELLABLE_STATUSES.includes(run.status),
|
isCancellable: CANCELLABLE_STATUSES.includes(run.status),
|
||||||
environment: {
|
environment: {
|
||||||
type: environment.type,
|
type: environment.type,
|
||||||
|
|||||||
+57
-20
@@ -1,4 +1,9 @@
|
|||||||
import { CloudArrowDownIcon, QueueListIcon, StopCircleIcon } from "@heroicons/react/20/solid";
|
import {
|
||||||
|
ArrowPathIcon,
|
||||||
|
CloudArrowDownIcon,
|
||||||
|
QueueListIcon,
|
||||||
|
StopCircleIcon,
|
||||||
|
} from "@heroicons/react/20/solid";
|
||||||
import { useParams } from "@remix-run/react";
|
import { useParams } from "@remix-run/react";
|
||||||
import { LoaderFunctionArgs } from "@remix-run/server-runtime";
|
import { LoaderFunctionArgs } from "@remix-run/server-runtime";
|
||||||
import { formatDurationNanoseconds, nanosecondsToMilliseconds } from "@trigger.dev/core/v3";
|
import { formatDurationNanoseconds, nanosecondsToMilliseconds } from "@trigger.dev/core/v3";
|
||||||
@@ -14,6 +19,7 @@ import { Paragraph } from "~/components/primitives/Paragraph";
|
|||||||
import { Property, PropertyTable } from "~/components/primitives/PropertyTable";
|
import { Property, PropertyTable } from "~/components/primitives/PropertyTable";
|
||||||
import { CancelRunDialog } from "~/components/runs/v3/CancelRunDialog";
|
import { CancelRunDialog } from "~/components/runs/v3/CancelRunDialog";
|
||||||
import { LiveTimer } from "~/components/runs/v3/LiveTimer";
|
import { LiveTimer } from "~/components/runs/v3/LiveTimer";
|
||||||
|
import { ReplayRunDialog } from "~/components/runs/v3/ReplayRunDialog";
|
||||||
import { RunIcon } from "~/components/runs/v3/RunIcon";
|
import { RunIcon } from "~/components/runs/v3/RunIcon";
|
||||||
import { SpanEvents } from "~/components/runs/v3/SpanEvents";
|
import { SpanEvents } from "~/components/runs/v3/SpanEvents";
|
||||||
import { SpanTitle } from "~/components/runs/v3/SpanTitle";
|
import { SpanTitle } from "~/components/runs/v3/SpanTitle";
|
||||||
@@ -22,7 +28,7 @@ import { TaskRunAttemptStatusCombo } from "~/components/runs/v3/TaskRunAttemptSt
|
|||||||
import { useOrganization } from "~/hooks/useOrganizations";
|
import { useOrganization } from "~/hooks/useOrganizations";
|
||||||
import { useProject } from "~/hooks/useProject";
|
import { useProject } from "~/hooks/useProject";
|
||||||
import { redirectWithErrorMessage } from "~/models/message.server";
|
import { redirectWithErrorMessage } from "~/models/message.server";
|
||||||
import { SpanPresenter } from "~/presenters/v3/SpanPresenter.server";
|
import { Span, SpanPresenter } from "~/presenters/v3/SpanPresenter.server";
|
||||||
import { requireUserId } from "~/services/session.server";
|
import { requireUserId } from "~/services/session.server";
|
||||||
import { cn } from "~/utils/cn";
|
import { cn } from "~/utils/cn";
|
||||||
import { v3RunPath, v3RunSpanPath, v3SpanParamsSchema, v3TraceSpanPath } from "~/utils/pathBuilder";
|
import { v3RunPath, v3RunSpanPath, v3SpanParamsSchema, v3TraceSpanPath } from "~/utils/pathBuilder";
|
||||||
@@ -188,24 +194,7 @@ export default function Page() {
|
|||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
<div className="flex items-center gap-4">
|
<div className="flex items-center gap-4">
|
||||||
{event.isPartial && runParam && (
|
<RunActionButtons span={event} />
|
||||||
<Dialog>
|
|
||||||
<DialogTrigger asChild>
|
|
||||||
<Button variant="danger/small" LeadingIcon={StopCircleIcon}>
|
|
||||||
Cancel run
|
|
||||||
</Button>
|
|
||||||
</DialogTrigger>
|
|
||||||
<CancelRunDialog
|
|
||||||
runFriendlyId={event.runId}
|
|
||||||
redirectPath={v3RunSpanPath(
|
|
||||||
organization,
|
|
||||||
project,
|
|
||||||
{ friendlyId: runParam },
|
|
||||||
{ spanId: event.spanId }
|
|
||||||
)}
|
|
||||||
/>
|
|
||||||
</Dialog>
|
|
||||||
)}
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
) : null}
|
) : null}
|
||||||
@@ -213,6 +202,54 @@ export default function Page() {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function RunActionButtons({ span }: { span: Span }) {
|
||||||
|
const organization = useOrganization();
|
||||||
|
const project = useProject();
|
||||||
|
const { runParam } = useParams();
|
||||||
|
|
||||||
|
if (!runParam) return null;
|
||||||
|
|
||||||
|
if (span.isPartial) {
|
||||||
|
return (
|
||||||
|
<Dialog>
|
||||||
|
<DialogTrigger asChild>
|
||||||
|
<Button variant="danger/small" LeadingIcon={StopCircleIcon}>
|
||||||
|
Cancel run
|
||||||
|
</Button>
|
||||||
|
</DialogTrigger>
|
||||||
|
<CancelRunDialog
|
||||||
|
runFriendlyId={span.runId}
|
||||||
|
redirectPath={v3RunSpanPath(
|
||||||
|
organization,
|
||||||
|
project,
|
||||||
|
{ friendlyId: runParam },
|
||||||
|
{ spanId: span.spanId }
|
||||||
|
)}
|
||||||
|
/>
|
||||||
|
</Dialog>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
return (
|
||||||
|
<Dialog>
|
||||||
|
<DialogTrigger asChild>
|
||||||
|
<Button variant="tertiary/small" LeadingIcon={ArrowPathIcon}>
|
||||||
|
Replay run
|
||||||
|
</Button>
|
||||||
|
</DialogTrigger>
|
||||||
|
<ReplayRunDialog
|
||||||
|
runFriendlyId={span.runId}
|
||||||
|
failedRedirect={v3RunSpanPath(
|
||||||
|
organization,
|
||||||
|
project,
|
||||||
|
{ friendlyId: runParam },
|
||||||
|
{ spanId: span.spanId }
|
||||||
|
)}
|
||||||
|
/>
|
||||||
|
</Dialog>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
function PacketDisplay({
|
function PacketDisplay({
|
||||||
data,
|
data,
|
||||||
dataType,
|
dataType,
|
||||||
|
|||||||
@@ -0,0 +1,72 @@
|
|||||||
|
import type { ActionFunctionArgs } from "@remix-run/server-runtime";
|
||||||
|
import { json } from "@remix-run/server-runtime";
|
||||||
|
import { PrismaErrorSchema, prisma } from "~/db.server";
|
||||||
|
import { z } from "zod";
|
||||||
|
import { authenticateApiRequest } from "~/services/apiAuth.server";
|
||||||
|
import { CancelRunService } from "~/services/runs/cancelRun.server";
|
||||||
|
import { ApiRunPresenter } from "~/presenters/ApiRunPresenter.server";
|
||||||
|
import { ReplayTaskRunService } from "~/v3/services/replayTaskRun.server";
|
||||||
|
import { logger } from "~/services/logger.server";
|
||||||
|
|
||||||
|
const ParamsSchema = z.object({
|
||||||
|
/* This is the run friendly ID */
|
||||||
|
runParam: z.string(),
|
||||||
|
});
|
||||||
|
|
||||||
|
export async function action({ request, params }: ActionFunctionArgs) {
|
||||||
|
// Ensure this is a POST request
|
||||||
|
if (request.method.toUpperCase() !== "POST") {
|
||||||
|
return { status: 405, body: "Method Not Allowed" };
|
||||||
|
}
|
||||||
|
|
||||||
|
// Authenticate the request
|
||||||
|
const authenticationResult = await authenticateApiRequest(request);
|
||||||
|
if (!authenticationResult) {
|
||||||
|
return json({ error: "Invalid or Missing API Key" }, { status: 401 });
|
||||||
|
}
|
||||||
|
|
||||||
|
const parsed = ParamsSchema.safeParse(params);
|
||||||
|
if (!parsed.success) {
|
||||||
|
return json({ error: "Invalid or missing run ID" }, { status: 400 });
|
||||||
|
}
|
||||||
|
|
||||||
|
const { runParam } = parsed.data;
|
||||||
|
|
||||||
|
try {
|
||||||
|
const taskRun = await prisma.taskRun.findUnique({
|
||||||
|
where: {
|
||||||
|
friendlyId: runParam,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
if (!taskRun) {
|
||||||
|
return json({ error: "Run not found" }, { status: 404 });
|
||||||
|
}
|
||||||
|
|
||||||
|
const service = new ReplayTaskRunService();
|
||||||
|
const newRun = await service.call(taskRun);
|
||||||
|
|
||||||
|
if (!newRun) {
|
||||||
|
return json({ error: "Failed to create new run" }, { status: 400 });
|
||||||
|
}
|
||||||
|
|
||||||
|
return json({
|
||||||
|
id: newRun?.friendlyId,
|
||||||
|
});
|
||||||
|
} catch (error) {
|
||||||
|
if (error instanceof Error) {
|
||||||
|
logger.error("Failed to replay run", {
|
||||||
|
error: {
|
||||||
|
name: error.name,
|
||||||
|
message: error.message,
|
||||||
|
stack: error.stack,
|
||||||
|
},
|
||||||
|
run: runParam,
|
||||||
|
});
|
||||||
|
return json({ error: error.message }, { status: 400 });
|
||||||
|
} else {
|
||||||
|
logger.error("Failed to replay run", { error: JSON.stringify(error), run: runParam });
|
||||||
|
return json({ error: JSON.stringify(error) }, { status: 400 });
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -25,7 +25,7 @@ export async function action({ request, params }: ActionFunctionArgs) {
|
|||||||
const parsed = ParamsSchema.safeParse(params);
|
const parsed = ParamsSchema.safeParse(params);
|
||||||
|
|
||||||
if (!parsed.success) {
|
if (!parsed.success) {
|
||||||
return json({ error: "Invalid or Missing runId" }, { status: 400 });
|
return json({ error: "Invalid or Missing run id" }, { status: 400 });
|
||||||
}
|
}
|
||||||
|
|
||||||
const { runParam } = parsed.data;
|
const { runParam } = parsed.data;
|
||||||
|
|||||||
@@ -0,0 +1,85 @@
|
|||||||
|
import { parse } from "@conform-to/zod";
|
||||||
|
import { ActionFunction, json } from "@remix-run/node";
|
||||||
|
import { z } from "zod";
|
||||||
|
import { prisma } from "~/db.server";
|
||||||
|
import { redirectWithErrorMessage, redirectWithSuccessMessage } from "~/models/message.server";
|
||||||
|
import { logger } from "~/services/logger.server";
|
||||||
|
import { v3RunPath } from "~/utils/pathBuilder";
|
||||||
|
import { ReplayTaskRunService } from "~/v3/services/replayTaskRun.server";
|
||||||
|
|
||||||
|
const FormSchema = z.object({
|
||||||
|
failedRedirect: z.string(),
|
||||||
|
});
|
||||||
|
|
||||||
|
const ParamSchema = z.object({
|
||||||
|
runParam: z.string(),
|
||||||
|
});
|
||||||
|
|
||||||
|
export const action: ActionFunction = async ({ request, params }) => {
|
||||||
|
const { runParam } = ParamSchema.parse(params);
|
||||||
|
|
||||||
|
const formData = await request.formData();
|
||||||
|
const submission = parse(formData, { schema: FormSchema });
|
||||||
|
|
||||||
|
if (!submission.value) {
|
||||||
|
return json(submission);
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
const taskRun = await prisma.taskRun.findUnique({
|
||||||
|
where: {
|
||||||
|
friendlyId: runParam,
|
||||||
|
},
|
||||||
|
include: {
|
||||||
|
project: {
|
||||||
|
include: {
|
||||||
|
organization: true,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
if (!taskRun) {
|
||||||
|
return redirectWithErrorMessage(submission.value.failedRedirect, request, "Run not found");
|
||||||
|
}
|
||||||
|
|
||||||
|
const replayRunService = new ReplayTaskRunService();
|
||||||
|
const newRun = await replayRunService.call(taskRun);
|
||||||
|
|
||||||
|
if (!newRun) {
|
||||||
|
return redirectWithErrorMessage(
|
||||||
|
submission.value.failedRedirect,
|
||||||
|
request,
|
||||||
|
"Failed to replay run"
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
const runPath = v3RunPath(
|
||||||
|
{
|
||||||
|
slug: taskRun.project.organization.slug,
|
||||||
|
},
|
||||||
|
{ slug: taskRun.project.slug },
|
||||||
|
{ friendlyId: newRun.friendlyId }
|
||||||
|
);
|
||||||
|
|
||||||
|
return redirectWithSuccessMessage(runPath, request, `Replaying run`);
|
||||||
|
} catch (error) {
|
||||||
|
if (error instanceof Error) {
|
||||||
|
logger.error("Failed to replay run", {
|
||||||
|
error: {
|
||||||
|
name: error.name,
|
||||||
|
message: error.message,
|
||||||
|
stack: error.stack,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
return redirectWithErrorMessage(submission.value.failedRedirect, request, error.message);
|
||||||
|
} else {
|
||||||
|
logger.error("Failed to replay run", { error });
|
||||||
|
return redirectWithErrorMessage(
|
||||||
|
submission.value.failedRedirect,
|
||||||
|
request,
|
||||||
|
JSON.stringify(error)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
@@ -0,0 +1,53 @@
|
|||||||
|
import { conditionallyImportPacket, parsePacket } from "@trigger.dev/core/v3";
|
||||||
|
import { Prisma, TaskRun } from "@trigger.dev/database";
|
||||||
|
import { findEnvironmentById } from "~/models/runtimeEnvironment.server";
|
||||||
|
import { logger } from "~/services/logger.server";
|
||||||
|
import { BaseService } from "./baseService.server";
|
||||||
|
import { TriggerTaskService } from "./triggerTask.server";
|
||||||
|
|
||||||
|
type ExtendedTaskRunAttempt = Prisma.TaskRunAttemptGetPayload<{
|
||||||
|
include: {
|
||||||
|
runtimeEnvironment: true;
|
||||||
|
backgroundWorker: true;
|
||||||
|
};
|
||||||
|
}>;
|
||||||
|
|
||||||
|
export class ReplayTaskRunService extends BaseService {
|
||||||
|
public async call(existingTaskRun: TaskRun) {
|
||||||
|
const authenticatedEnvironment = await findEnvironmentById(
|
||||||
|
existingTaskRun.runtimeEnvironmentId
|
||||||
|
);
|
||||||
|
if (!authenticatedEnvironment) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
logger.info("Replaying task run", {
|
||||||
|
taskRunId: existingTaskRun.id,
|
||||||
|
taskRunFriendlyId: existingTaskRun.friendlyId,
|
||||||
|
});
|
||||||
|
|
||||||
|
const payloadPacket = await conditionallyImportPacket({
|
||||||
|
data: existingTaskRun.payload,
|
||||||
|
dataType: existingTaskRun.payloadType,
|
||||||
|
});
|
||||||
|
const parsedPayload = await parsePacket(payloadPacket);
|
||||||
|
|
||||||
|
logger.info("Replaying task run payload", {
|
||||||
|
taskRunId: existingTaskRun.id,
|
||||||
|
taskRunFriendlyId: existingTaskRun.friendlyId,
|
||||||
|
payloadPacketType: payloadPacket.dataType,
|
||||||
|
});
|
||||||
|
|
||||||
|
const triggerTaskService = new TriggerTaskService();
|
||||||
|
return await triggerTaskService.call(existingTaskRun.taskIdentifier, authenticatedEnvironment, {
|
||||||
|
payload: parsedPayload,
|
||||||
|
options: {
|
||||||
|
queue: {
|
||||||
|
name: existingTaskRun.queue,
|
||||||
|
},
|
||||||
|
concurrencyKey: existingTaskRun.concurrencyKey ?? undefined,
|
||||||
|
test: existingTaskRun.isTest,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
+11
-1
@@ -1,7 +1,7 @@
|
|||||||
{
|
{
|
||||||
"$schema": "https://mintlify.com/schema.json",
|
"$schema": "https://mintlify.com/schema.json",
|
||||||
"name": "Trigger.dev",
|
"name": "Trigger.dev",
|
||||||
"openapi": ["/openapi.yml"],
|
"openapi": ["/openapi.yml", "/v3-openapi.json"],
|
||||||
"versions": ["v3 (Developer Preview)", "v2"],
|
"versions": ["v3 (Developer Preview)", "v2"],
|
||||||
"logo": {
|
"logo": {
|
||||||
"dark": "/logo/dark.png",
|
"dark": "/logo/dark.png",
|
||||||
@@ -586,6 +586,16 @@
|
|||||||
"v3/reference-cli-build",
|
"v3/reference-cli-build",
|
||||||
"v3/reference-cli-who-am-i"
|
"v3/reference-cli-who-am-i"
|
||||||
]
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"group": "Management API",
|
||||||
|
"pages": [
|
||||||
|
"v3/management-start-run",
|
||||||
|
"v3/management-get-run",
|
||||||
|
"v3/management-get-runs",
|
||||||
|
"v3/management-replay-run",
|
||||||
|
"v3/management-cancel-run"
|
||||||
|
]
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -0,0 +1,216 @@
|
|||||||
|
{
|
||||||
|
"openapi": "3.1.0",
|
||||||
|
"info": {
|
||||||
|
"title": "Trigger.dev v3 REST API",
|
||||||
|
"description": "The REST API lets you trigger and manage runs on Trigger.dev. You can trigger a run, get the status of a run, and get the results of a run. ",
|
||||||
|
"version": "2024-04"
|
||||||
|
},
|
||||||
|
"servers": [
|
||||||
|
{
|
||||||
|
"url": "https://api.trigger.dev",
|
||||||
|
"description": "Trigger.dev API"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"paths": {
|
||||||
|
"/api/v1/runs/{run_id}/replay": {
|
||||||
|
"post": {
|
||||||
|
"description": "Creates a new run with the same payload and options as the original run.",
|
||||||
|
"parameters": [
|
||||||
|
{
|
||||||
|
"in": "path",
|
||||||
|
"name": "run_id",
|
||||||
|
"required": true,
|
||||||
|
"schema": {
|
||||||
|
"type": "string"
|
||||||
|
},
|
||||||
|
"description": "The ID of an existing run. When you trigger a run you will get an id in the response."
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"responses": {
|
||||||
|
"200": {
|
||||||
|
"description": "Successful request",
|
||||||
|
"content": {
|
||||||
|
"application/json": {
|
||||||
|
"schema": {
|
||||||
|
"type": "object",
|
||||||
|
"properties": {
|
||||||
|
"id": {
|
||||||
|
"type": "string",
|
||||||
|
"description": "The ID of the new run."
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"400": {
|
||||||
|
"description": "Invalid request",
|
||||||
|
"content": {
|
||||||
|
"application/json": {
|
||||||
|
"schema": {
|
||||||
|
"type": "object",
|
||||||
|
"properties": {
|
||||||
|
"error": {
|
||||||
|
"type": "string",
|
||||||
|
"enum": ["Invalid or missing run ID", "Failed to create new run"]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"401": {
|
||||||
|
"description": "Unauthorized request",
|
||||||
|
"content": {
|
||||||
|
"application/json": {
|
||||||
|
"schema": {
|
||||||
|
"type": "object",
|
||||||
|
"properties": {
|
||||||
|
"error": {
|
||||||
|
"type": "string",
|
||||||
|
"enum": ["Invalid or Missing API key"]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"404": {
|
||||||
|
"description": "Resource not found",
|
||||||
|
"content": {
|
||||||
|
"application/json": {
|
||||||
|
"schema": {
|
||||||
|
"type": "object",
|
||||||
|
"properties": {
|
||||||
|
"error": {
|
||||||
|
"type": "string",
|
||||||
|
"enum": ["Run not found"]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"tags": ["run"],
|
||||||
|
"security": [{ "bearerAuth": [] }],
|
||||||
|
"operationId": "replay_run_v1",
|
||||||
|
"x-codeSamples": [
|
||||||
|
{
|
||||||
|
"lang": "typescript",
|
||||||
|
"source": "const handle = await runs.replay(existingRun.id);"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"lang": "sh",
|
||||||
|
"source": "curl --request POST \\\n\t--url https://api.trigger.dev/api/v1/runs/{run_id}/replay \\\n\t--header 'Authorization: Bearer <token>'"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"/api/v1/runs/{run_id}/cancel": {
|
||||||
|
"post": {
|
||||||
|
"description": "Cancels a run.",
|
||||||
|
"parameters": [
|
||||||
|
{
|
||||||
|
"in": "path",
|
||||||
|
"name": "run_id",
|
||||||
|
"required": true,
|
||||||
|
"schema": {
|
||||||
|
"type": "string"
|
||||||
|
},
|
||||||
|
"description": "The ID of an existing run. When you trigger a run you will get an id in the response."
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"responses": {
|
||||||
|
"200": {
|
||||||
|
"description": "Successful request",
|
||||||
|
"content": {
|
||||||
|
"application/json": {
|
||||||
|
"schema": {
|
||||||
|
"type": "object",
|
||||||
|
"properties": {
|
||||||
|
"message": {
|
||||||
|
"type": "string",
|
||||||
|
"description": "Confirmation message that the run was canceled."
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"400": {
|
||||||
|
"description": "Invalid request",
|
||||||
|
"content": {
|
||||||
|
"application/json": {
|
||||||
|
"schema": {
|
||||||
|
"type": "object",
|
||||||
|
"properties": {
|
||||||
|
"error": {
|
||||||
|
"type": "string",
|
||||||
|
"enum": ["Invalid or missing run ID", "Failed to create new run"]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"401": {
|
||||||
|
"description": "Unauthorized request",
|
||||||
|
"content": {
|
||||||
|
"application/json": {
|
||||||
|
"schema": {
|
||||||
|
"type": "object",
|
||||||
|
"properties": {
|
||||||
|
"error": {
|
||||||
|
"type": "string",
|
||||||
|
"enum": ["Invalid or Missing API key"]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"404": {
|
||||||
|
"description": "Resource not found",
|
||||||
|
"content": {
|
||||||
|
"application/json": {
|
||||||
|
"schema": {
|
||||||
|
"type": "object",
|
||||||
|
"properties": {
|
||||||
|
"error": {
|
||||||
|
"type": "string",
|
||||||
|
"enum": ["Run not found"]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"tags": ["run"],
|
||||||
|
"security": [{ "bearerAuth": [] }],
|
||||||
|
"operationId": "replay_run_v1",
|
||||||
|
"x-codeSamples": [
|
||||||
|
{
|
||||||
|
"lang": "typescript",
|
||||||
|
"source": "await runs.cancel(existingRun.id);"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"lang": "sh",
|
||||||
|
"source": "curl --request POST \\\n\t--url https://api.trigger.dev/api/v1/runs/{run_id}/cancel \\\n\t--header 'Authorization: Bearer <token>'"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"components": {
|
||||||
|
"securitySchemes": {
|
||||||
|
"bearerAuth": {
|
||||||
|
"type": "http",
|
||||||
|
"scheme": "bearer",
|
||||||
|
"description": "Use your Secret API key in the form 'Bearer <SECRET KEY>' (without the quotation marks)"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"security": [{ "bearerAuth": [] }]
|
||||||
|
}
|
||||||
@@ -0,0 +1,4 @@
|
|||||||
|
---
|
||||||
|
title: "Cancel run"
|
||||||
|
openapi: "v3-openapi POST /api/v1/runs/{run_id}/cancel"
|
||||||
|
---
|
||||||
@@ -0,0 +1,6 @@
|
|||||||
|
---
|
||||||
|
title: "Get run"
|
||||||
|
description: "Get a run using the Task id."
|
||||||
|
---
|
||||||
|
|
||||||
|
<Snippet file="incomplete-docs.mdx" />
|
||||||
@@ -0,0 +1,6 @@
|
|||||||
|
---
|
||||||
|
title: "Get runs"
|
||||||
|
description: "Get runs using a Task id."
|
||||||
|
---
|
||||||
|
|
||||||
|
<Snippet file="incomplete-docs.mdx" />
|
||||||
@@ -0,0 +1,4 @@
|
|||||||
|
---
|
||||||
|
title: "Replay run"
|
||||||
|
openapi: "v3-openapi POST /api/v1/runs/{run_id}/replay"
|
||||||
|
---
|
||||||
@@ -0,0 +1,6 @@
|
|||||||
|
---
|
||||||
|
title: "Start run"
|
||||||
|
description: "Start a run using the Task id, payload and options."
|
||||||
|
---
|
||||||
|
|
||||||
|
<Snippet file="incomplete-docs.mdx" />
|
||||||
@@ -9,7 +9,10 @@ import {
|
|||||||
BatchTriggerTaskRequestBody,
|
BatchTriggerTaskRequestBody,
|
||||||
BatchTriggerTaskResponse,
|
BatchTriggerTaskResponse,
|
||||||
CreateUploadPayloadUrlResponseBody,
|
CreateUploadPayloadUrlResponseBody,
|
||||||
|
ReplayRunResponse,
|
||||||
|
CanceledRunResponse,
|
||||||
} from "../schemas";
|
} from "../schemas";
|
||||||
|
import { z } from "zod";
|
||||||
|
|
||||||
export type TriggerOptions = {
|
export type TriggerOptions = {
|
||||||
spanParentAsLink?: boolean;
|
spanParentAsLink?: boolean;
|
||||||
@@ -88,6 +91,30 @@ export class ApiClient {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
replayRun(runId: string) {
|
||||||
|
return zodfetch(
|
||||||
|
ReplayRunResponse,
|
||||||
|
`${this.baseUrl}/api/v1/runs/${runId}/replay`,
|
||||||
|
{
|
||||||
|
method: "POST",
|
||||||
|
headers: this.#getHeaders(false),
|
||||||
|
},
|
||||||
|
zodFetchOptions
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
cancelRun(runId: string) {
|
||||||
|
return zodfetch(
|
||||||
|
CanceledRunResponse,
|
||||||
|
`${this.baseUrl}/api/v2/runs/${runId}/cancel`,
|
||||||
|
{
|
||||||
|
method: "POST",
|
||||||
|
headers: this.#getHeaders(false),
|
||||||
|
},
|
||||||
|
zodFetchOptions
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
#getHeaders(spanParentAsLink: boolean) {
|
#getHeaders(spanParentAsLink: boolean) {
|
||||||
const headers: Record<string, string> = {
|
const headers: Record<string, string> = {
|
||||||
"Content-Type": "application/json",
|
"Content-Type": "application/json",
|
||||||
|
|||||||
@@ -198,3 +198,15 @@ export const CreateUploadPayloadUrlResponseBody = z.object({
|
|||||||
});
|
});
|
||||||
|
|
||||||
export type CreateUploadPayloadUrlResponseBody = z.infer<typeof CreateUploadPayloadUrlResponseBody>;
|
export type CreateUploadPayloadUrlResponseBody = z.infer<typeof CreateUploadPayloadUrlResponseBody>;
|
||||||
|
|
||||||
|
export const ReplayRunResponse = z.object({
|
||||||
|
id: z.string(),
|
||||||
|
});
|
||||||
|
|
||||||
|
export type ReplayRunResponse = z.infer<typeof ReplayRunResponse>;
|
||||||
|
|
||||||
|
export const CanceledRunResponse = z.object({
|
||||||
|
message: z.string(),
|
||||||
|
});
|
||||||
|
|
||||||
|
export type CanceledRunResponse = z.infer<typeof CanceledRunResponse>;
|
||||||
|
|||||||
@@ -8,3 +8,5 @@ import type { Context } from "./shared";
|
|||||||
export type { Context };
|
export type { Context };
|
||||||
|
|
||||||
export { logger, type LogLevel } from "@trigger.dev/core/v3";
|
export { logger, type LogLevel } from "@trigger.dev/core/v3";
|
||||||
|
|
||||||
|
export { runs } from "./management";
|
||||||
|
|||||||
@@ -0,0 +1,39 @@
|
|||||||
|
import { CanceledRunResponse, ReplayRunResponse, apiClientManager } from "@trigger.dev/core/v3";
|
||||||
|
import { apiClientMissingError } from "./shared";
|
||||||
|
|
||||||
|
export const runs = {
|
||||||
|
replay: replayRun,
|
||||||
|
cancel: cancelRun,
|
||||||
|
};
|
||||||
|
|
||||||
|
async function replayRun(runId: string): Promise<ReplayRunResponse> {
|
||||||
|
const apiClient = apiClientManager.client;
|
||||||
|
|
||||||
|
if (!apiClient) {
|
||||||
|
throw apiClientMissingError();
|
||||||
|
}
|
||||||
|
|
||||||
|
const response = await apiClient.replayRun(runId);
|
||||||
|
|
||||||
|
if (!response.ok) {
|
||||||
|
throw new Error(response.error);
|
||||||
|
}
|
||||||
|
|
||||||
|
return response.data;
|
||||||
|
}
|
||||||
|
|
||||||
|
async function cancelRun(runId: string): Promise<CanceledRunResponse> {
|
||||||
|
const apiClient = apiClientManager.client;
|
||||||
|
|
||||||
|
if (!apiClient) {
|
||||||
|
throw apiClientMissingError();
|
||||||
|
}
|
||||||
|
|
||||||
|
const response = await apiClient.cancelRun(runId);
|
||||||
|
|
||||||
|
if (!response.ok) {
|
||||||
|
throw new Error(response.error);
|
||||||
|
}
|
||||||
|
|
||||||
|
return response.data;
|
||||||
|
}
|
||||||
@@ -562,7 +562,7 @@ async function handleTaskRunExecutionResult<TOutput>(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function apiClientMissingError() {
|
export function apiClientMissingError() {
|
||||||
const hasBaseUrl = !!apiClientManager.baseURL;
|
const hasBaseUrl = !!apiClientManager.baseURL;
|
||||||
const hasAccessToken = !!apiClientManager.accessToken;
|
const hasAccessToken = !!apiClientManager.accessToken;
|
||||||
if (!hasBaseUrl && !hasAccessToken) {
|
if (!hasBaseUrl && !hasAccessToken) {
|
||||||
|
|||||||
Generated
+48
-18
@@ -3071,6 +3071,9 @@ importers:
|
|||||||
'@trigger.dev/sdk':
|
'@trigger.dev/sdk':
|
||||||
specifier: workspace:^3.0.0-beta.0
|
specifier: workspace:^3.0.0-beta.0
|
||||||
version: link:../../packages/trigger-sdk
|
version: link:../../packages/trigger-sdk
|
||||||
|
dotenv:
|
||||||
|
specifier: ^16.4.5
|
||||||
|
version: 16.4.5
|
||||||
execa:
|
execa:
|
||||||
specifier: ^8.0.1
|
specifier: ^8.0.1
|
||||||
version: 8.0.1
|
version: 8.0.1
|
||||||
@@ -3096,6 +3099,9 @@ importers:
|
|||||||
trigger.dev:
|
trigger.dev:
|
||||||
specifier: workspace:*
|
specifier: workspace:*
|
||||||
version: link:../../packages/cli-v3
|
version: link:../../packages/cli-v3
|
||||||
|
ts-node:
|
||||||
|
specifier: ^10.9.2
|
||||||
|
version: 10.9.2(@types/node@20.4.2)(typescript@5.3.3)
|
||||||
typescript:
|
typescript:
|
||||||
specifier: ^5.3.0
|
specifier: ^5.3.0
|
||||||
version: 5.3.3
|
version: 5.3.3
|
||||||
@@ -17875,18 +17881,6 @@ packages:
|
|||||||
dependencies:
|
dependencies:
|
||||||
ms: 2.1.2
|
ms: 2.1.2
|
||||||
|
|
||||||
/debug@4.3.4:
|
|
||||||
resolution: {integrity: sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ==}
|
|
||||||
engines: {node: '>=6.0'}
|
|
||||||
peerDependencies:
|
|
||||||
supports-color: '*'
|
|
||||||
peerDependenciesMeta:
|
|
||||||
supports-color:
|
|
||||||
optional: true
|
|
||||||
dependencies:
|
|
||||||
ms: 2.1.2
|
|
||||||
dev: false
|
|
||||||
|
|
||||||
/debug@4.3.4(supports-color@8.1.1):
|
/debug@4.3.4(supports-color@8.1.1):
|
||||||
resolution: {integrity: sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ==}
|
resolution: {integrity: sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ==}
|
||||||
engines: {node: '>=6.0'}
|
engines: {node: '>=6.0'}
|
||||||
@@ -18303,6 +18297,11 @@ packages:
|
|||||||
resolution: {integrity: sha512-XvPXc8XAQThSjAbY6cQ/9PcBXmFoWuw1sQ3b8HqUCR6ziGXjkTi//kB9SWa2UwqlgdAIuRqAa/9hVljzPehbYg==}
|
resolution: {integrity: sha512-XvPXc8XAQThSjAbY6cQ/9PcBXmFoWuw1sQ3b8HqUCR6ziGXjkTi//kB9SWa2UwqlgdAIuRqAa/9hVljzPehbYg==}
|
||||||
engines: {node: '>=12'}
|
engines: {node: '>=12'}
|
||||||
|
|
||||||
|
/dotenv@16.4.5:
|
||||||
|
resolution: {integrity: sha512-ZmdL2rui+eB2YwhsWzjInR8LldtZHGDoQ1ugH85ppHKwpUHL7j7rN0Ti9NCnGiQbhaZ11FpR+7ao1dNsmduNUg==}
|
||||||
|
engines: {node: '>=12'}
|
||||||
|
dev: false
|
||||||
|
|
||||||
/dotenv@8.6.0:
|
/dotenv@8.6.0:
|
||||||
resolution: {integrity: sha512-IrPdXQsk2BbzvCBGBOTmmSH5SodmqZNt4ERAZDmW4CT+tL8VtvinqywuANaFu4bOMWki16nqf0e4oC0QIaDr/g==}
|
resolution: {integrity: sha512-IrPdXQsk2BbzvCBGBOTmmSH5SodmqZNt4ERAZDmW4CT+tL8VtvinqywuANaFu4bOMWki16nqf0e4oC0QIaDr/g==}
|
||||||
engines: {node: '>=10'}
|
engines: {node: '>=10'}
|
||||||
@@ -19362,7 +19361,7 @@ packages:
|
|||||||
eslint-import-resolver-webpack:
|
eslint-import-resolver-webpack:
|
||||||
optional: true
|
optional: true
|
||||||
dependencies:
|
dependencies:
|
||||||
'@typescript-eslint/parser': 5.59.6(eslint@8.42.0)(typescript@5.0.4)
|
'@typescript-eslint/parser': 5.59.6(eslint@8.42.0)(typescript@5.3.3)
|
||||||
debug: 3.2.7(supports-color@5.5.0)
|
debug: 3.2.7(supports-color@5.5.0)
|
||||||
eslint: 8.42.0
|
eslint: 8.42.0
|
||||||
eslint-import-resolver-node: 0.3.7
|
eslint-import-resolver-node: 0.3.7
|
||||||
@@ -19505,7 +19504,7 @@ packages:
|
|||||||
'@typescript-eslint/parser':
|
'@typescript-eslint/parser':
|
||||||
optional: true
|
optional: true
|
||||||
dependencies:
|
dependencies:
|
||||||
'@typescript-eslint/parser': 5.59.6(eslint@8.42.0)(typescript@5.0.4)
|
'@typescript-eslint/parser': 5.59.6(eslint@8.42.0)(typescript@5.3.3)
|
||||||
array-includes: 3.1.6
|
array-includes: 3.1.6
|
||||||
array.prototype.flat: 1.3.1
|
array.prototype.flat: 1.3.1
|
||||||
array.prototype.flatmap: 1.3.1
|
array.prototype.flatmap: 1.3.1
|
||||||
@@ -24992,7 +24991,7 @@ packages:
|
|||||||
hasBin: true
|
hasBin: true
|
||||||
dependencies:
|
dependencies:
|
||||||
acorn: 8.10.0
|
acorn: 8.10.0
|
||||||
acorn-walk: 8.2.0
|
acorn-walk: 8.3.2
|
||||||
capnp-ts: 0.7.0
|
capnp-ts: 0.7.0
|
||||||
exit-hook: 2.2.1
|
exit-hook: 2.2.1
|
||||||
glob-to-regexp: 0.4.1
|
glob-to-regexp: 0.4.1
|
||||||
@@ -25015,7 +25014,7 @@ packages:
|
|||||||
hasBin: true
|
hasBin: true
|
||||||
dependencies:
|
dependencies:
|
||||||
acorn: 8.10.0
|
acorn: 8.10.0
|
||||||
acorn-walk: 8.2.0
|
acorn-walk: 8.3.2
|
||||||
capnp-ts: 0.7.0
|
capnp-ts: 0.7.0
|
||||||
exit-hook: 2.2.1
|
exit-hook: 2.2.1
|
||||||
glob-to-regexp: 0.4.1
|
glob-to-regexp: 0.4.1
|
||||||
@@ -25038,7 +25037,7 @@ packages:
|
|||||||
hasBin: true
|
hasBin: true
|
||||||
dependencies:
|
dependencies:
|
||||||
acorn: 8.10.0
|
acorn: 8.10.0
|
||||||
acorn-walk: 8.2.0
|
acorn-walk: 8.3.2
|
||||||
capnp-ts: 0.7.0
|
capnp-ts: 0.7.0
|
||||||
exit-hook: 2.2.1
|
exit-hook: 2.2.1
|
||||||
glob-to-regexp: 0.4.1
|
glob-to-regexp: 0.4.1
|
||||||
@@ -28941,7 +28940,7 @@ packages:
|
|||||||
resolution: {integrity: sha512-OScOjQjrrjhAdFpQmnkE/qbIBGCRFhQB/YaJhcC3CPOlmhe7llnW46Ac1J5+EjcNXOTnDdpF96Erw/yedsGksQ==}
|
resolution: {integrity: sha512-OScOjQjrrjhAdFpQmnkE/qbIBGCRFhQB/YaJhcC3CPOlmhe7llnW46Ac1J5+EjcNXOTnDdpF96Erw/yedsGksQ==}
|
||||||
engines: {node: '>=8.6.0'}
|
engines: {node: '>=8.6.0'}
|
||||||
dependencies:
|
dependencies:
|
||||||
debug: 4.3.4
|
debug: 4.3.4(supports-color@8.1.1)
|
||||||
module-details-from-path: 1.0.3
|
module-details-from-path: 1.0.3
|
||||||
resolve: 1.22.4
|
resolve: 1.22.4
|
||||||
transitivePeerDependencies:
|
transitivePeerDependencies:
|
||||||
@@ -31374,6 +31373,37 @@ packages:
|
|||||||
yn: 3.1.1
|
yn: 3.1.1
|
||||||
dev: true
|
dev: true
|
||||||
|
|
||||||
|
/ts-node@10.9.2(@types/node@20.4.2)(typescript@5.3.3):
|
||||||
|
resolution: {integrity: sha512-f0FFpIdcHgn8zcPSbf1dRevwt047YMnaiJM3u2w2RewrB+fob/zePZcrOyQoLMMO7aBIddLcQIEK5dYjkLnGrQ==}
|
||||||
|
hasBin: true
|
||||||
|
peerDependencies:
|
||||||
|
'@swc/core': '>=1.2.50'
|
||||||
|
'@swc/wasm': '>=1.2.50'
|
||||||
|
'@types/node': '*'
|
||||||
|
typescript: '>=2.7'
|
||||||
|
peerDependenciesMeta:
|
||||||
|
'@swc/core':
|
||||||
|
optional: true
|
||||||
|
'@swc/wasm':
|
||||||
|
optional: true
|
||||||
|
dependencies:
|
||||||
|
'@cspotcode/source-map-support': 0.8.1
|
||||||
|
'@tsconfig/node10': 1.0.9
|
||||||
|
'@tsconfig/node12': 1.0.11
|
||||||
|
'@tsconfig/node14': 1.0.3
|
||||||
|
'@tsconfig/node16': 1.0.3
|
||||||
|
'@types/node': 20.4.2
|
||||||
|
acorn: 8.10.0
|
||||||
|
acorn-walk: 8.3.2
|
||||||
|
arg: 4.1.3
|
||||||
|
create-require: 1.1.1
|
||||||
|
diff: 4.0.2
|
||||||
|
make-error: 1.3.6
|
||||||
|
typescript: 5.3.3
|
||||||
|
v8-compile-cache-lib: 3.0.1
|
||||||
|
yn: 3.1.1
|
||||||
|
dev: true
|
||||||
|
|
||||||
/ts-poet@6.6.0:
|
/ts-poet@6.6.0:
|
||||||
resolution: {integrity: sha512-4vEH/wkhcjRPFOdBwIh9ItO6jOoumVLRF4aABDX5JSNEubSqwOulihxQPqai+OkuygJm3WYMInxXQX4QwVNMuw==}
|
resolution: {integrity: sha512-4vEH/wkhcjRPFOdBwIh9ItO6jOoumVLRF4aABDX5JSNEubSqwOulihxQPqai+OkuygJm3WYMInxXQX4QwVNMuw==}
|
||||||
dependencies:
|
dependencies:
|
||||||
|
|||||||
@@ -3,7 +3,8 @@
|
|||||||
"version": "0.1.0",
|
"version": "0.1.0",
|
||||||
"private": true,
|
"private": true,
|
||||||
"scripts": {
|
"scripts": {
|
||||||
"dev:trigger": "triggerdev dev"
|
"dev:trigger": "triggerdev dev",
|
||||||
|
"management": "ts-node ./src/management.ts"
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@ffmpeg-installer/ffmpeg": "^1.1.0",
|
"@ffmpeg-installer/ffmpeg": "^1.1.0",
|
||||||
@@ -13,6 +14,7 @@
|
|||||||
"@traceloop/instrumentation-openai": "^0.3.9",
|
"@traceloop/instrumentation-openai": "^0.3.9",
|
||||||
"@trigger.dev/core": "workspace:^3.0.0-beta.0",
|
"@trigger.dev/core": "workspace:^3.0.0-beta.0",
|
||||||
"@trigger.dev/sdk": "workspace:^3.0.0-beta.0",
|
"@trigger.dev/sdk": "workspace:^3.0.0-beta.0",
|
||||||
|
"dotenv": "^16.4.5",
|
||||||
"execa": "^8.0.1",
|
"execa": "^8.0.1",
|
||||||
"msw": "^2.2.1",
|
"msw": "^2.2.1",
|
||||||
"openai": "^4.28.0",
|
"openai": "^4.28.0",
|
||||||
@@ -23,6 +25,7 @@
|
|||||||
"@trigger.dev/tsconfig": "workspace:*",
|
"@trigger.dev/tsconfig": "workspace:*",
|
||||||
"@types/node": "20.4.2",
|
"@types/node": "20.4.2",
|
||||||
"trigger.dev": "workspace:*",
|
"trigger.dev": "workspace:*",
|
||||||
|
"ts-node": "^10.9.2",
|
||||||
"typescript": "^5.3.0"
|
"typescript": "^5.3.0"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,16 @@
|
|||||||
|
import { runs } from "@trigger.dev/sdk/v3";
|
||||||
|
import { simpleChildTask } from "./trigger/subtasks";
|
||||||
|
import dotenv from "dotenv";
|
||||||
|
|
||||||
|
dotenv.config();
|
||||||
|
|
||||||
|
export async function run() {
|
||||||
|
const run = await simpleChildTask.trigger({ payload: { message: "Hello, World!" } });
|
||||||
|
const canceled = await runs.cancel(run.id);
|
||||||
|
console.log("canceled run", canceled);
|
||||||
|
|
||||||
|
const replayed = await runs.replay(run.id);
|
||||||
|
console.log("replayed run", replayed);
|
||||||
|
}
|
||||||
|
|
||||||
|
run();
|
||||||
Reference in New Issue
Block a user