@opengeni/api-router 0.22.2 → 0.26.1
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/app.d.ts +2 -2
- package/dist/app.js +1 -1
- package/dist/auth/managed-auth.d.ts +0 -30
- package/dist/browser-controller-authority.d.ts +43 -0
- package/dist/browser-state-authority.d.ts +27 -0
- package/dist/{chunk-HWXJW5C7.js → chunk-JIKNR5YL.js} +27993 -14546
- package/dist/chunk-JIKNR5YL.js.map +1 -0
- package/dist/codemode.d.ts +23 -0
- package/dist/editable-artifact-live-hints.d.ts +11 -0
- package/dist/editable-artifact-native-kernel.d.ts +37 -0
- package/dist/editable-artifact-office-import.d.ts +22 -0
- package/dist/editable-artifact-production.d.ts +29 -0
- package/dist/editable-artifact-websocket.d.ts +49 -0
- package/dist/editable-artifact-workspace-files.d.ts +23 -0
- package/dist/github-browser-flow.d.ts +6 -0
- package/dist/http/cors.d.ts +1 -0
- package/dist/http/sse.d.ts +2 -0
- package/dist/index.d.ts +3 -2
- package/dist/index.js +1824 -30
- package/dist/index.js.map +1 -1
- package/dist/integrations/api-integrations.d.ts +24 -0
- package/dist/integrations/atlassian.d.ts +176 -0
- package/dist/integrations/github-skill-source.d.ts +5 -0
- package/dist/integrations/google-drive.d.ts +85 -0
- package/dist/integrations/oauth-client.d.ts +30 -1
- package/dist/integrations/provider-oauth.d.ts +19 -0
- package/dist/integrations/slack-bot.d.ts +4 -0
- package/dist/integrations/slack-interactions.d.ts +14 -2
- package/dist/integrations/social-api.d.ts +2 -1
- package/dist/mcp/editable-artifact-query-schema.d.ts +4 -0
- package/dist/mcp/editable-artifacts.d.ts +13 -0
- package/dist/mcp/receipts.d.ts +28 -0
- package/dist/mcp/scheduled-task-view.d.ts +518 -0
- package/dist/mcp/server.d.ts +14 -3
- package/dist/memory-slack-delivery.d.ts +9 -0
- package/dist/routes/api-integrations.d.ts +8 -0
- package/dist/routes/browser-identities.d.ts +5 -0
- package/dist/routes/browser-sessions.d.ts +6 -0
- package/dist/routes/company-profile.d.ts +3 -0
- package/dist/routes/computer-sessions.d.ts +6 -0
- package/dist/routes/editable-artifacts.d.ts +44 -0
- package/dist/routes/integration-features.d.ts +3 -0
- package/dist/routes/memory-slack-publications.d.ts +6 -0
- package/dist/routes/plugins.d.ts +8 -0
- package/dist/routes/sessions.d.ts +17 -2
- package/dist/routes/skills.d.ts +6 -0
- package/dist/routes/video-generation.d.ts +3 -0
- package/dist/sandbox/auth-callout.d.ts +2 -0
- package/dist/sandbox/channel-a.d.ts +59 -2
- package/dist/sandbox/metrics-ingestion.d.ts +6 -1
- package/dist/sandbox/viewer.d.ts +4 -2
- package/dist/temporal-schedule-cleanup.d.ts +26 -0
- package/package.json +19 -14
- package/src/app.ts +277 -41
- package/src/auth/managed-auth.ts +0 -16
- package/src/browser-controller-authority.ts +137 -0
- package/src/browser-state-authority.ts +236 -0
- package/src/codemode.ts +186 -0
- package/src/editable-artifact-live-hints.ts +64 -0
- package/src/editable-artifact-native-kernel.ts +659 -0
- package/src/editable-artifact-office-import.ts +230 -0
- package/src/editable-artifact-production.ts +419 -0
- package/src/editable-artifact-websocket.ts +311 -0
- package/src/editable-artifact-workspace-files.ts +186 -0
- package/src/github-browser-flow.ts +35 -6
- package/src/http/auth.ts +2 -0
- package/src/http/cors.ts +3 -0
- package/src/http/sse.ts +101 -6
- package/src/index.ts +147 -23
- package/src/integrations/api-integrations.ts +350 -0
- package/src/integrations/atlassian.ts +1621 -0
- package/src/integrations/github-skill-source.ts +142 -0
- package/src/integrations/google-drive.ts +1000 -64
- package/src/integrations/oauth-client.ts +159 -89
- package/src/integrations/provider-oauth.ts +777 -0
- package/src/integrations/slack-bot.ts +31 -2
- package/src/integrations/slack-interactions.ts +610 -42
- package/src/integrations/social-api.ts +11 -0
- package/src/mcp/documents.ts +74 -26
- package/src/mcp/editable-artifact-query-schema.ts +236 -0
- package/src/mcp/editable-artifacts.ts +448 -0
- package/src/mcp/receipts.ts +95 -0
- package/src/mcp/scheduled-task-view.ts +642 -0
- package/src/mcp/server.ts +1718 -310
- package/src/memory-slack-delivery.ts +209 -0
- package/src/observability.ts +3 -3
- package/src/routes/api-integrations.ts +407 -0
- package/src/routes/api-keys.ts +7 -1
- package/src/routes/browser-identities.ts +136 -0
- package/src/routes/browser-sessions.ts +2543 -0
- package/src/routes/codex.ts +7 -4
- package/src/routes/company-profile.ts +255 -0
- package/src/routes/computer-sessions.ts +1247 -0
- package/src/routes/connections.ts +358 -102
- package/src/routes/documents.ts +22 -3
- package/src/routes/editable-artifacts.ts +1159 -0
- package/src/routes/enrollments.ts +54 -12
- package/src/routes/environments.ts +60 -11
- package/src/routes/files.ts +277 -65
- package/src/routes/github.ts +18 -2
- package/src/routes/install.ts +38 -2
- package/src/routes/integration-features.ts +258 -0
- package/src/routes/machines.ts +1 -1
- package/src/routes/memory-slack-publications.ts +216 -0
- package/src/routes/packs.ts +437 -7
- package/src/routes/plugins.ts +751 -0
- package/src/routes/rigs.ts +77 -20
- package/src/routes/scheduled-tasks.ts +94 -42
- package/src/routes/sessions.ts +475 -234
- package/src/routes/skills.ts +174 -0
- package/src/routes/transcription-recordings.ts +65 -33
- package/src/routes/video-generation.ts +132 -0
- package/src/routes/workspaces.ts +46 -24
- package/src/sandbox/auth-callout.ts +16 -4
- package/src/sandbox/channel-a.ts +809 -85
- package/src/sandbox/enrollment.ts +13 -3
- package/src/sandbox/machines.ts +1 -1
- package/src/sandbox/metrics-ingestion.ts +121 -3
- package/src/sandbox/rematerialize.ts +35 -47
- package/src/sandbox/viewer.ts +58 -29
- package/src/temporal-schedule-cleanup.ts +135 -0
- package/dist/chunk-HWXJW5C7.js.map +0 -1
- package/dist/mcp/toolspace.d.ts +0 -62
- package/src/mcp/toolspace.ts +0 -1186
package/src/routes/rigs.ts
CHANGED
|
@@ -7,7 +7,6 @@ import {
|
|
|
7
7
|
import {
|
|
8
8
|
beginRigChangeVerificationAttempt,
|
|
9
9
|
listRigs,
|
|
10
|
-
RigChangeAlreadyVerifyingError,
|
|
11
10
|
RigChangeTransitionError,
|
|
12
11
|
} from "@opengeni/db";
|
|
13
12
|
import type { Hono } from "hono";
|
|
@@ -32,38 +31,87 @@ import { boundedLimit } from "../http/common";
|
|
|
32
31
|
export function registerRigRoutes(app: Hono, deps: ApiRouteDeps): void {
|
|
33
32
|
const { db, workflowClient } = deps;
|
|
34
33
|
|
|
35
|
-
async function startChangeVerification(
|
|
34
|
+
async function startChangeVerification(
|
|
35
|
+
workspaceId: string,
|
|
36
|
+
changeId: string,
|
|
37
|
+
): Promise<{
|
|
38
|
+
change: Awaited<ReturnType<typeof beginRigChangeVerificationAttempt>>;
|
|
39
|
+
started: boolean;
|
|
40
|
+
}> {
|
|
36
41
|
const startedAt = new Date().toISOString();
|
|
37
42
|
let change;
|
|
38
43
|
try {
|
|
39
|
-
change = await beginRigChangeVerificationAttempt(db, workspaceId, changeId, {
|
|
44
|
+
change = await beginRigChangeVerificationAttempt(db, workspaceId, changeId, {
|
|
45
|
+
startedAt,
|
|
46
|
+
// Reuse the existing attempt after an ambiguous Temporal start. Its
|
|
47
|
+
// deterministic workflow id turns this into an idempotent recovery.
|
|
48
|
+
allowAlreadyVerifying: true,
|
|
49
|
+
});
|
|
40
50
|
} catch (error) {
|
|
41
|
-
if (
|
|
42
|
-
error instanceof RigChangeAlreadyVerifyingError ||
|
|
43
|
-
error instanceof RigChangeTransitionError
|
|
44
|
-
) {
|
|
51
|
+
if (error instanceof RigChangeTransitionError) {
|
|
45
52
|
throw new HTTPException(409, { message: error.message });
|
|
46
53
|
}
|
|
47
54
|
throw error;
|
|
48
55
|
}
|
|
49
56
|
const attempt =
|
|
50
57
|
typeof change.verification?.attempt === "number" ? change.verification.attempt : Date.now();
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
58
|
+
try {
|
|
59
|
+
await workflowClient.startRigVerification({
|
|
60
|
+
workspaceId,
|
|
61
|
+
changeId,
|
|
62
|
+
workflowId: `rig-verification-change-${changeId}-attempt-${attempt}`,
|
|
63
|
+
});
|
|
64
|
+
return { change, started: true };
|
|
65
|
+
} catch (error) {
|
|
66
|
+
// The verifying transition already committed. Keep its deterministic
|
|
67
|
+
// attempt identity retryable instead of reporting an opaque 5xx after a
|
|
68
|
+
// durable mutation or inventing a second attempt after an ambiguous start.
|
|
69
|
+
deps.observability?.warn("rig change verification start failed", {
|
|
70
|
+
workspaceId,
|
|
71
|
+
changeId,
|
|
72
|
+
attempt,
|
|
73
|
+
error: error instanceof Error ? error.message : String(error),
|
|
74
|
+
});
|
|
75
|
+
return { change, started: false };
|
|
76
|
+
}
|
|
57
77
|
}
|
|
58
78
|
|
|
59
|
-
async function startVersionVerification(
|
|
79
|
+
async function startVersionVerification(
|
|
80
|
+
workspaceId: string,
|
|
81
|
+
versionId: string,
|
|
82
|
+
workflowId = `rig-verification-version-${versionId}-${crypto.randomUUID()}`,
|
|
83
|
+
): Promise<void> {
|
|
60
84
|
await workflowClient.startRigVerification({
|
|
61
85
|
workspaceId,
|
|
62
86
|
versionId,
|
|
63
|
-
workflowId
|
|
87
|
+
workflowId,
|
|
64
88
|
});
|
|
65
89
|
}
|
|
66
90
|
|
|
91
|
+
async function tryStartInitialVersionVerification(
|
|
92
|
+
workspaceId: string,
|
|
93
|
+
versionId: string,
|
|
94
|
+
): Promise<boolean> {
|
|
95
|
+
try {
|
|
96
|
+
await startVersionVerification(
|
|
97
|
+
workspaceId,
|
|
98
|
+
versionId,
|
|
99
|
+
`rig-verification-version-${versionId}-initial`,
|
|
100
|
+
);
|
|
101
|
+
return true;
|
|
102
|
+
} catch (error) {
|
|
103
|
+
// Creation already committed. The rig remains fully usable through its
|
|
104
|
+
// runtime setup fallback and can be re-verified explicitly; never turn a
|
|
105
|
+
// successful create into an unretryable 5xx because Temporal was down.
|
|
106
|
+
deps.observability?.warn("initial rig provider-image verification start failed", {
|
|
107
|
+
workspaceId,
|
|
108
|
+
versionId,
|
|
109
|
+
error: error instanceof Error ? error.message : String(error),
|
|
110
|
+
});
|
|
111
|
+
return false;
|
|
112
|
+
}
|
|
113
|
+
}
|
|
114
|
+
|
|
67
115
|
app.get("/v1/workspaces/:workspaceId/rigs", async (c) => {
|
|
68
116
|
const workspaceId = c.req.param("workspaceId");
|
|
69
117
|
await requireAccessGrant(c, deps, workspaceId, "rigs:use");
|
|
@@ -75,6 +123,10 @@ export function registerRigRoutes(app: Hono, deps: ApiRouteDeps): void {
|
|
|
75
123
|
const grant = await requireAccessGrant(c, deps, workspaceId, "rigs:manage");
|
|
76
124
|
const payload = CreateRigRequest.parse(await c.req.json());
|
|
77
125
|
const rig = await createRigForApi({ db }, grant, payload);
|
|
126
|
+
if (rig.activeVersion) {
|
|
127
|
+
const started = await tryStartInitialVersionVerification(workspaceId, rig.activeVersion.id);
|
|
128
|
+
if (!started) c.header("OpenGeni-Rig-Verification", "deferred");
|
|
129
|
+
}
|
|
78
130
|
return c.json(rig, 201);
|
|
79
131
|
});
|
|
80
132
|
|
|
@@ -112,7 +164,10 @@ export function registerRigRoutes(app: Hono, deps: ApiRouteDeps): void {
|
|
|
112
164
|
const grant = await requireAccessGrant(c, deps, workspaceId, "rigs:manage");
|
|
113
165
|
const rig = await requireRigForApi(db, workspaceId, c.req.param("rigId"));
|
|
114
166
|
const payload = RigDefinitionEditPayload.parse(await c.req.json());
|
|
115
|
-
|
|
167
|
+
const version = await createRigVersionForApi({ db }, grant, rig, payload);
|
|
168
|
+
const started = await tryStartInitialVersionVerification(workspaceId, version.id);
|
|
169
|
+
if (!started) c.header("OpenGeni-Rig-Verification", "deferred");
|
|
170
|
+
return c.json(version, 201);
|
|
116
171
|
});
|
|
117
172
|
|
|
118
173
|
// Rollback / promote-activate: flips which existing version is active.
|
|
@@ -142,8 +197,9 @@ export function registerRigRoutes(app: Hono, deps: ApiRouteDeps): void {
|
|
|
142
197
|
const rig = await requireRigForApi(db, workspaceId, c.req.param("rigId"));
|
|
143
198
|
const request = ProposeRigChangeRequest.parse(await c.req.json());
|
|
144
199
|
const change = await proposeRigChangeForApi({ db }, grant, rig, request);
|
|
145
|
-
const
|
|
146
|
-
|
|
200
|
+
const verification = await startChangeVerification(workspaceId, change.id);
|
|
201
|
+
if (!verification.started) c.header("OpenGeni-Rig-Verification", "deferred");
|
|
202
|
+
return c.json(verification.change, 201);
|
|
147
203
|
});
|
|
148
204
|
|
|
149
205
|
app.get("/v1/workspaces/:workspaceId/rigs/:rigId/changes/:changeId", async (c) => {
|
|
@@ -163,8 +219,9 @@ export function registerRigRoutes(app: Hono, deps: ApiRouteDeps): void {
|
|
|
163
219
|
c.req.param("rigId"),
|
|
164
220
|
c.req.param("changeId"),
|
|
165
221
|
);
|
|
166
|
-
const
|
|
167
|
-
|
|
222
|
+
const verification = await startChangeVerification(workspaceId, change.id);
|
|
223
|
+
if (!verification.started) c.header("OpenGeni-Rig-Verification", "deferred");
|
|
224
|
+
return c.json(verification.change, 202);
|
|
168
225
|
});
|
|
169
226
|
|
|
170
227
|
app.post("/v1/workspaces/:workspaceId/rigs/:rigId/changes/:changeId/promote", async (c) => {
|
|
@@ -10,6 +10,7 @@ import {
|
|
|
10
10
|
updateScheduledTask,
|
|
11
11
|
} from "@opengeni/db";
|
|
12
12
|
import type { Hono } from "hono";
|
|
13
|
+
import { HTTPException } from "hono/http-exception";
|
|
13
14
|
import { requireAccessGrant } from "@opengeni/core";
|
|
14
15
|
import { recordWorkspaceUsage, requireLimit } from "@opengeni/core";
|
|
15
16
|
import type { ApiRouteDeps } from "@opengeni/core";
|
|
@@ -19,13 +20,18 @@ import {
|
|
|
19
20
|
manualScheduledTaskTriggerUsageKey,
|
|
20
21
|
manualScheduledTaskTriggerWorkflowId,
|
|
21
22
|
scheduledTaskToolsProvided,
|
|
23
|
+
scheduledTaskForGrant,
|
|
24
|
+
scheduledTaskRunForGrant,
|
|
22
25
|
scheduledTaskTriggerToken,
|
|
23
26
|
requireScheduledTaskForApi,
|
|
24
27
|
syncCreatedScheduledTask,
|
|
25
28
|
syncUpdatedScheduledTask,
|
|
29
|
+
validateScheduledTaskTarget,
|
|
26
30
|
validatedScheduledTaskUpdate,
|
|
27
31
|
} from "@opengeni/core";
|
|
28
32
|
import { boundedLimit } from "../http/common";
|
|
33
|
+
import { revokeKnowledgeSourceScheduleAuthorization } from "../integrations/google-drive";
|
|
34
|
+
import { revokeAtlassianScheduleAuthorization } from "../integrations/atlassian";
|
|
29
35
|
|
|
30
36
|
export function registerScheduledTaskRoutes(app: Hono, deps: ApiRouteDeps): void {
|
|
31
37
|
const { settings, db, workflowClient, objectStorage } = deps;
|
|
@@ -34,7 +40,13 @@ export function registerScheduledTaskRoutes(app: Hono, deps: ApiRouteDeps): void
|
|
|
34
40
|
const workspaceId = c.req.param("workspaceId");
|
|
35
41
|
const grant = await requireAccessGrant(c, deps, workspaceId, "scheduled_tasks:manage");
|
|
36
42
|
const rawPayload = await c.req.json();
|
|
37
|
-
const
|
|
43
|
+
const parsedPayload = CreateScheduledTaskRequest.safeParse(rawPayload);
|
|
44
|
+
if (!parsedPayload.success) {
|
|
45
|
+
throw new HTTPException(400, {
|
|
46
|
+
message: "invalid scheduled task create request",
|
|
47
|
+
});
|
|
48
|
+
}
|
|
49
|
+
const payload = parsedPayload.data;
|
|
38
50
|
await requireLimit(deps, {
|
|
39
51
|
accountId: grant.accountId,
|
|
40
52
|
workspaceId,
|
|
@@ -48,21 +60,25 @@ export function registerScheduledTaskRoutes(app: Hono, deps: ApiRouteDeps): void
|
|
|
48
60
|
grant,
|
|
49
61
|
payload,
|
|
50
62
|
toolsProvided: scheduledTaskToolsProvided(rawPayload),
|
|
63
|
+
sessionAuthorization: deps.sessionAuthorization,
|
|
64
|
+
authorizationSurface: "http",
|
|
51
65
|
});
|
|
52
66
|
await syncCreatedScheduledTask({ db, workflowClient, task });
|
|
53
|
-
return c.json(task, 201);
|
|
67
|
+
return c.json(scheduledTaskForGrant(task, grant), 201);
|
|
54
68
|
});
|
|
55
69
|
|
|
56
70
|
app.get("/v1/workspaces/:workspaceId/scheduled-tasks", async (c) => {
|
|
57
71
|
const workspaceId = c.req.param("workspaceId");
|
|
58
|
-
await requireAccessGrant(c, deps, workspaceId, "scheduled_tasks:run");
|
|
59
|
-
|
|
72
|
+
const grant = await requireAccessGrant(c, deps, workspaceId, "scheduled_tasks:run");
|
|
73
|
+
const tasks = await listScheduledTasks(db, workspaceId, boundedLimit(c.req.query("limit")));
|
|
74
|
+
return c.json(tasks.map((task) => scheduledTaskForGrant(task, grant)));
|
|
60
75
|
});
|
|
61
76
|
|
|
62
77
|
app.get("/v1/workspaces/:workspaceId/scheduled-tasks/:taskId", async (c) => {
|
|
63
78
|
const workspaceId = c.req.param("workspaceId");
|
|
64
|
-
await requireAccessGrant(c, deps, workspaceId, "scheduled_tasks:run");
|
|
65
|
-
|
|
79
|
+
const grant = await requireAccessGrant(c, deps, workspaceId, "scheduled_tasks:run");
|
|
80
|
+
const task = await requireScheduledTaskForApi(db, workspaceId, c.req.param("taskId"));
|
|
81
|
+
return c.json(scheduledTaskForGrant(task, grant));
|
|
66
82
|
});
|
|
67
83
|
|
|
68
84
|
app.patch("/v1/workspaces/:workspaceId/scheduled-tasks/:taskId", async (c) => {
|
|
@@ -72,7 +88,13 @@ export function registerScheduledTaskRoutes(app: Hono, deps: ApiRouteDeps): void
|
|
|
72
88
|
const existing = await requireScheduledTaskForApi(db, workspaceId, taskId);
|
|
73
89
|
const previous = await captureScheduledTaskRestoreState(db, existing);
|
|
74
90
|
const rawPayload = await c.req.json();
|
|
75
|
-
const
|
|
91
|
+
const parsedPayload = UpdateScheduledTaskRequest.safeParse(rawPayload);
|
|
92
|
+
if (!parsedPayload.success) {
|
|
93
|
+
throw new HTTPException(400, {
|
|
94
|
+
message: "invalid scheduled task update request",
|
|
95
|
+
});
|
|
96
|
+
}
|
|
97
|
+
const payload = parsedPayload.data;
|
|
76
98
|
const update = await validatedScheduledTaskUpdate({
|
|
77
99
|
settings,
|
|
78
100
|
db,
|
|
@@ -81,30 +103,36 @@ export function registerScheduledTaskRoutes(app: Hono, deps: ApiRouteDeps): void
|
|
|
81
103
|
existing,
|
|
82
104
|
payload,
|
|
83
105
|
toolsProvided: scheduledTaskToolsProvided(rawPayload),
|
|
106
|
+
sessionAuthorization: deps.sessionAuthorization,
|
|
107
|
+
authorizationSurface: "http",
|
|
84
108
|
});
|
|
85
109
|
const task = await updateScheduledTask(db, workspaceId, taskId, update);
|
|
86
110
|
await syncUpdatedScheduledTask({ db, workflowClient, previous, task });
|
|
87
|
-
return c.json(task);
|
|
111
|
+
return c.json(scheduledTaskForGrant(task, grant));
|
|
88
112
|
});
|
|
89
113
|
|
|
90
114
|
app.post("/v1/workspaces/:workspaceId/scheduled-tasks/:taskId/pause", async (c) => {
|
|
91
115
|
const workspaceId = c.req.param("workspaceId");
|
|
92
|
-
await requireAccessGrant(c, deps, workspaceId, "scheduled_tasks:manage");
|
|
116
|
+
const grant = await requireAccessGrant(c, deps, workspaceId, "scheduled_tasks:manage");
|
|
93
117
|
const existing = await requireScheduledTaskForApi(db, workspaceId, c.req.param("taskId"));
|
|
94
118
|
const previous = await captureScheduledTaskRestoreState(db, existing);
|
|
95
|
-
const task = await updateScheduledTask(db, workspaceId, existing.id, {
|
|
119
|
+
const task = await updateScheduledTask(db, workspaceId, existing.id, {
|
|
120
|
+
status: "paused",
|
|
121
|
+
});
|
|
96
122
|
await syncUpdatedScheduledTask({ db, workflowClient, previous, task });
|
|
97
|
-
return c.json(task);
|
|
123
|
+
return c.json(scheduledTaskForGrant(task, grant));
|
|
98
124
|
});
|
|
99
125
|
|
|
100
126
|
app.post("/v1/workspaces/:workspaceId/scheduled-tasks/:taskId/resume", async (c) => {
|
|
101
127
|
const workspaceId = c.req.param("workspaceId");
|
|
102
|
-
await requireAccessGrant(c, deps, workspaceId, "scheduled_tasks:manage");
|
|
128
|
+
const grant = await requireAccessGrant(c, deps, workspaceId, "scheduled_tasks:manage");
|
|
103
129
|
const existing = await requireScheduledTaskForApi(db, workspaceId, c.req.param("taskId"));
|
|
104
130
|
const previous = await captureScheduledTaskRestoreState(db, existing);
|
|
105
|
-
const task = await updateScheduledTask(db, workspaceId, existing.id, {
|
|
131
|
+
const task = await updateScheduledTask(db, workspaceId, existing.id, {
|
|
132
|
+
status: "active",
|
|
133
|
+
});
|
|
106
134
|
await syncUpdatedScheduledTask({ db, workflowClient, previous, task });
|
|
107
|
-
return c.json(task);
|
|
135
|
+
return c.json(scheduledTaskForGrant(task, grant));
|
|
108
136
|
});
|
|
109
137
|
|
|
110
138
|
app.post("/v1/workspaces/:workspaceId/scheduled-tasks/:taskId/trigger", async (c) => {
|
|
@@ -113,23 +141,36 @@ export function registerScheduledTaskRoutes(app: Hono, deps: ApiRouteDeps): void
|
|
|
113
141
|
// Load the task before the gate so a codex-model scheduled task can be
|
|
114
142
|
// recognised as codex-billed and skip the credit/cost gates at the edge.
|
|
115
143
|
const task = await requireScheduledTaskForApi(db, workspaceId, c.req.param("taskId"));
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
144
|
+
if (task.action.kind === "agent_turn") {
|
|
145
|
+
await validateScheduledTaskTarget({
|
|
146
|
+
db,
|
|
147
|
+
sessionAuthorization: deps.sessionAuthorization,
|
|
148
|
+
authorizationSurface: "http",
|
|
149
|
+
grant,
|
|
150
|
+
targetSessionId: task.targetSessionId,
|
|
151
|
+
runMode: task.runMode,
|
|
152
|
+
variableSetId: task.variableSetId,
|
|
153
|
+
rigId: task.rigId,
|
|
154
|
+
agentConfig: task.agentConfig,
|
|
155
|
+
missingTargetStatus: 404,
|
|
156
|
+
});
|
|
157
|
+
await requireLimit(deps, {
|
|
158
|
+
accountId: grant.accountId,
|
|
159
|
+
workspaceId,
|
|
160
|
+
action: "agent_run:create",
|
|
161
|
+
quantity: 1,
|
|
162
|
+
model: task.agentConfig.model ?? deps.settings.openaiModel,
|
|
163
|
+
});
|
|
164
|
+
}
|
|
123
165
|
// Body is optional (a bare POST is still a valid trigger); only a present,
|
|
124
166
|
// non-empty body must parse against the contract.
|
|
125
167
|
const body = await c.req.json().catch(() => ({}));
|
|
126
168
|
const { triggerId } = TriggerScheduledTaskRequest.parse(body ?? {});
|
|
127
169
|
const triggerToken = scheduledTaskTriggerToken(triggerId);
|
|
128
|
-
const agentRunUsageIdempotencyKey =
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
);
|
|
170
|
+
const agentRunUsageIdempotencyKey =
|
|
171
|
+
task.action.kind === "agent_turn"
|
|
172
|
+
? manualScheduledTaskTriggerUsageKey(workspaceId, task.id, triggerToken)
|
|
173
|
+
: `knowledge-source-sync:manual:${workspaceId}:${task.id}:${triggerToken}`;
|
|
133
174
|
const triggerWorkflowId = manualScheduledTaskTriggerWorkflowId(task.id, triggerToken);
|
|
134
175
|
await workflowClient.triggerScheduledTask({
|
|
135
176
|
task,
|
|
@@ -137,24 +178,31 @@ export function registerScheduledTaskRoutes(app: Hono, deps: ApiRouteDeps): void
|
|
|
137
178
|
triggerWorkflowId,
|
|
138
179
|
initiator: { kind: "subject", subjectId: grant.subjectId },
|
|
139
180
|
});
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
181
|
+
if (task.action.kind === "agent_turn") {
|
|
182
|
+
await recordWorkspaceUsage(deps, {
|
|
183
|
+
accountId: grant.accountId,
|
|
184
|
+
workspaceId,
|
|
185
|
+
subjectId: grant.subjectId,
|
|
186
|
+
eventType: "agent_run.created",
|
|
187
|
+
quantity: 1,
|
|
188
|
+
unit: "run",
|
|
189
|
+
sourceResourceType: "scheduled_task",
|
|
190
|
+
sourceResourceId: task.id,
|
|
191
|
+
idempotencyKey: agentRunUsageIdempotencyKey,
|
|
192
|
+
});
|
|
193
|
+
}
|
|
194
|
+
return c.json(scheduledTaskForGrant(task, grant), 202);
|
|
152
195
|
});
|
|
153
196
|
|
|
154
197
|
app.delete("/v1/workspaces/:workspaceId/scheduled-tasks/:taskId", async (c) => {
|
|
155
198
|
const workspaceId = c.req.param("workspaceId");
|
|
156
|
-
await requireAccessGrant(c, deps, workspaceId, "scheduled_tasks:manage");
|
|
199
|
+
const grant = await requireAccessGrant(c, deps, workspaceId, "scheduled_tasks:manage");
|
|
157
200
|
const task = await requireScheduledTaskForApi(db, workspaceId, c.req.param("taskId"));
|
|
201
|
+
if (task.metadata.connectorKind === "atlassian") {
|
|
202
|
+
await revokeAtlassianScheduleAuthorization(deps, { task, subjectId: grant.subjectId });
|
|
203
|
+
} else {
|
|
204
|
+
await revokeKnowledgeSourceScheduleAuthorization(deps, { task, subjectId: grant.subjectId });
|
|
205
|
+
}
|
|
158
206
|
await workflowClient.deleteScheduledTaskSchedule({
|
|
159
207
|
temporalScheduleId: task.temporalScheduleId,
|
|
160
208
|
});
|
|
@@ -164,10 +212,14 @@ export function registerScheduledTaskRoutes(app: Hono, deps: ApiRouteDeps): void
|
|
|
164
212
|
|
|
165
213
|
app.get("/v1/workspaces/:workspaceId/scheduled-tasks/:taskId/runs", async (c) => {
|
|
166
214
|
const workspaceId = c.req.param("workspaceId");
|
|
167
|
-
await requireAccessGrant(c, deps, workspaceId, "scheduled_tasks:run");
|
|
215
|
+
const grant = await requireAccessGrant(c, deps, workspaceId, "scheduled_tasks:run");
|
|
168
216
|
const task = await requireScheduledTaskForApi(db, workspaceId, c.req.param("taskId"));
|
|
169
|
-
|
|
170
|
-
|
|
217
|
+
const taskRuns = await listScheduledTaskRuns(
|
|
218
|
+
db,
|
|
219
|
+
workspaceId,
|
|
220
|
+
task.id,
|
|
221
|
+
boundedLimit(c.req.query("limit")),
|
|
171
222
|
);
|
|
223
|
+
return c.json(taskRuns.map((run) => scheduledTaskRunForGrant(run, grant)));
|
|
172
224
|
});
|
|
173
225
|
}
|