@mastra/factory 0.10.0-alpha.1 → 0.10.0-alpha.13
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/CHANGELOG.md +212 -0
- package/dist/auth.d.ts.map +1 -1
- package/dist/auth.js +2 -1
- package/dist/auth.js.map +1 -1
- package/dist/factory.d.ts +8 -0
- package/dist/factory.d.ts.map +1 -1
- package/dist/factory.js +25 -8
- package/dist/factory.js.map +1 -1
- package/dist/index.d.ts +2 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +2 -1
- package/dist/integrations/base.d.ts +3 -1
- package/dist/integrations/base.d.ts.map +1 -1
- package/dist/integrations/github/integration.d.ts +14 -0
- package/dist/integrations/github/integration.d.ts.map +1 -1
- package/dist/integrations/github/integration.js +48 -1
- package/dist/integrations/github/integration.js.map +1 -1
- package/dist/integrations/github/provenance.d.ts +7 -2
- package/dist/integrations/github/provenance.d.ts.map +1 -1
- package/dist/integrations/github/provenance.js +16 -2
- package/dist/integrations/github/provenance.js.map +1 -1
- package/dist/integrations/github/routes.d.ts +8 -1
- package/dist/integrations/github/routes.d.ts.map +1 -1
- package/dist/integrations/github/routes.js +228 -7
- package/dist/integrations/github/routes.js.map +1 -1
- package/dist/integrations/github/rules.d.ts.map +1 -1
- package/dist/integrations/github/rules.js +193 -127
- package/dist/integrations/github/rules.js.map +1 -1
- package/dist/integrations/github/session-subscriptions.d.ts +9 -0
- package/dist/integrations/github/session-subscriptions.d.ts.map +1 -1
- package/dist/integrations/github/session-subscriptions.js +39 -1
- package/dist/integrations/github/session-subscriptions.js.map +1 -1
- package/dist/integrations/linear/routes.d.ts.map +1 -1
- package/dist/integrations/linear/routes.js +50 -0
- package/dist/integrations/linear/routes.js.map +1 -1
- package/dist/integrations/platform/github/integration.d.ts +3 -1
- package/dist/integrations/platform/github/integration.d.ts.map +1 -1
- package/dist/integrations/platform/github/integration.js +33 -0
- package/dist/integrations/platform/github/integration.js.map +1 -1
- package/dist/routes/attention.d.ts +17 -0
- package/dist/routes/attention.d.ts.map +1 -0
- package/dist/routes/attention.js +305 -0
- package/dist/routes/attention.js.map +1 -0
- package/dist/routes/intake.d.ts +5 -0
- package/dist/routes/intake.d.ts.map +1 -1
- package/dist/routes/intake.js +70 -19
- package/dist/routes/intake.js.map +1 -1
- package/dist/routes/surface.d.ts +6 -5
- package/dist/routes/surface.d.ts.map +1 -1
- package/dist/routes/surface.js +62 -43
- package/dist/routes/surface.js.map +1 -1
- package/dist/routes/work-items.d.ts.map +1 -1
- package/dist/routes/work-items.js +35 -14
- package/dist/routes/work-items.js.map +1 -1
- package/dist/rules/defaults.js +5 -5
- package/dist/rules/defaults.js.map +1 -1
- package/dist/rules/dispatch-errors.d.ts +13 -0
- package/dist/rules/dispatch-errors.d.ts.map +1 -0
- package/dist/rules/dispatch-errors.js +77 -0
- package/dist/rules/dispatch-errors.js.map +1 -0
- package/dist/rules/dispatcher.d.ts +9 -0
- package/dist/rules/dispatcher.d.ts.map +1 -1
- package/dist/rules/dispatcher.js +118 -45
- package/dist/rules/dispatcher.js.map +1 -1
- package/dist/rules/processor.d.ts.map +1 -1
- package/dist/rules/processor.js +30 -14
- package/dist/rules/processor.js.map +1 -1
- package/dist/rules/terminal-cleanup.d.ts +1 -1
- package/dist/rules/terminal-cleanup.d.ts.map +1 -1
- package/dist/rules/terminal-cleanup.js +2 -2
- package/dist/rules/terminal-cleanup.js.map +1 -1
- package/dist/rules/tools.d.ts.map +1 -1
- package/dist/rules/tools.js +9 -5
- package/dist/rules/tools.js.map +1 -1
- package/dist/rules/transition-service.d.ts +5 -2
- package/dist/rules/transition-service.d.ts.map +1 -1
- package/dist/rules/transition-service.js +15 -2
- package/dist/rules/transition-service.js.map +1 -1
- package/dist/rules/types.d.ts +6 -1
- package/dist/rules/types.d.ts.map +1 -1
- package/dist/rules/types.js +25 -1
- package/dist/rules/types.js.map +1 -1
- package/dist/secret-encryption.d.ts +25 -0
- package/dist/secret-encryption.d.ts.map +1 -0
- package/dist/secret-encryption.js +96 -0
- package/dist/secret-encryption.js.map +1 -0
- package/dist/session/factory-session.d.ts +13 -3
- package/dist/session/factory-session.d.ts.map +1 -1
- package/dist/session/factory-session.js +15 -6
- package/dist/session/factory-session.js.map +1 -1
- package/dist/session/first-exec-capture.d.ts +16 -10
- package/dist/session/first-exec-capture.d.ts.map +1 -1
- package/dist/session/first-exec-capture.js +74 -14
- package/dist/session/first-exec-capture.js.map +1 -1
- package/dist/session/first-message-capture.d.ts +14 -10
- package/dist/session/first-message-capture.d.ts.map +1 -1
- package/dist/session/first-message-capture.js +17 -11
- package/dist/session/first-message-capture.js.map +1 -1
- package/dist/session/session-title.d.ts +7 -0
- package/dist/session/session-title.d.ts.map +1 -0
- package/dist/session/session-title.js +14 -0
- package/dist/session/session-title.js.map +1 -0
- package/dist/session/thread-title-mirror.d.ts +35 -0
- package/dist/session/thread-title-mirror.d.ts.map +1 -0
- package/dist/session/thread-title-mirror.js +51 -0
- package/dist/session/thread-title-mirror.js.map +1 -0
- package/dist/storage/domains/credentials/base.d.ts +3 -1
- package/dist/storage/domains/credentials/base.d.ts.map +1 -1
- package/dist/storage/domains/credentials/base.js +33 -15
- package/dist/storage/domains/credentials/base.js.map +1 -1
- package/dist/storage/domains/custom-providers/base.d.ts +3 -1
- package/dist/storage/domains/custom-providers/base.d.ts.map +1 -1
- package/dist/storage/domains/custom-providers/base.js +45 -25
- package/dist/storage/domains/custom-providers/base.js.map +1 -1
- package/dist/storage/domains/integrations/base.d.ts +3 -1
- package/dist/storage/domains/integrations/base.d.ts.map +1 -1
- package/dist/storage/domains/integrations/base.js +28 -12
- package/dist/storage/domains/integrations/base.js.map +1 -1
- package/dist/storage/domains/source-control/base.d.ts +8 -0
- package/dist/storage/domains/source-control/base.d.ts.map +1 -1
- package/dist/storage/domains/source-control/base.js +14 -2
- package/dist/storage/domains/source-control/base.js.map +1 -1
- package/dist/storage/domains/source-control/inmemory.d.ts +4 -0
- package/dist/storage/domains/source-control/inmemory.d.ts.map +1 -1
- package/dist/storage/domains/source-control/inmemory.js +7 -0
- package/dist/storage/domains/source-control/inmemory.js.map +1 -1
- package/dist/storage/domains/work-items/base.d.ts +120 -12
- package/dist/storage/domains/work-items/base.d.ts.map +1 -1
- package/dist/storage/domains/work-items/base.js +514 -44
- package/dist/storage/domains/work-items/base.js.map +1 -1
- package/dist/work-item-branch.d.ts +23 -0
- package/dist/work-item-branch.d.ts.map +1 -0
- package/dist/work-item-branch.js +39 -0
- package/dist/work-item-branch.js.map +1 -0
- package/factory-skills/factory-rereview/SKILL.md +2 -0
- package/factory-skills/factory-review/SKILL.md +2 -0
- package/factory-skills/factory-triage/SKILL.md +7 -17
- package/package.json +5 -5
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"provenance.js","names":[],"sources":["../../../src/integrations/github/provenance.ts"],"sourcesContent":["import type { FactoryRuleJsonValue } from '../../rules/types.js';\nimport type { IntegrationStorageHandle } from '../../storage/domains/integrations/base.js';\nimport type { SourceControlStorageHandle } from '../../storage/domains/source-control/base.js';\nimport type { FactoryRunBindingRecord, WorkItemRow } from '../../storage/domains/work-items/base.js';\nimport type { GithubIntegration } from './integration.js';\nimport { parseCreatedPullRequest } from './session-subscriptions.js';\n\nexport interface RecordFactoryPullRequestProvenanceInput {\n binding: FactoryRunBindingRecord;\n item: WorkItemRow;\n assistantMessageId: string;\n toolCallId: string;\n toolName: string;\n toolInput: FactoryRuleJsonValue;\n toolResult: FactoryRuleJsonValue;\n status: 'success' | 'error';\n}\n\nexport interface FactoryPullRequestProvenanceData {\n kind: 'factory-pr-provenance';\n bindingId: string;\n workItemId: string;\n repositoryId: number;\n pullRequestNumber: number;\n pullRequestUrl: string;\n assistantMessageId: string;\n toolCallId: string;\n}\n\nexport async function recordFactoryPullRequestProvenance(\n github: GithubIntegration,\n sourceControl: SourceControlStorageHandle,\n integrationStorage: IntegrationStorageHandle<\n Record<string, unknown>,\n Record<string, unknown>,\n FactoryPullRequestProvenanceData\n >,\n input: RecordFactoryPullRequestProvenanceInput,\n): Promise<void> {\n if (input.status !== 'success' || input.item.externalSource?.type === 'pull-request') return;\n const url = parseCreatedPullRequest({\n toolName: input.toolName,\n input: input.toolInput,\n output: input.toolResult,\n });\n if (!url) return;\n\n try {\n const match = url.match(/^https:\\/\\/github\\.com\\/([^/]+\\/[^/]+)\\/pull\\/(\\d+)\\/?$/i);\n if (!match) return;\n const repositorySlug = match[1]!;\n let repositoryId: number | undefined;\n let installationId: number | undefined;\n for (const connection of await sourceControl.connections.list({\n orgId: input.binding.orgId,\n factoryProjectId: input.binding.factoryProjectId,\n })) {\n const installation = await sourceControl.installations.get({\n orgId: input.binding.orgId,\n id: connection.installationId,\n });\n if (!installation) continue;\n for (const link of await sourceControl.projectRepositories.list({\n orgId: input.binding.orgId,\n connectionId: connection.id,\n })) {\n const repository = await sourceControl.repositories.get({ orgId: input.binding.orgId, id: link.repositoryId });\n if (!repository || repository.slug.toLowerCase() !== repositorySlug.toLowerCase()) continue;\n repositoryId = Number(repository.externalId);\n installationId = Number(installation.externalId);\n break;\n }\n if (repositoryId !== undefined) break;\n }\n const pullRequestNumber = Number(match[2]);\n const [owner, repo] = repositorySlug.split('/');\n if (\n !owner ||\n !repo ||\n repositoryId === undefined ||\n installationId === undefined ||\n !Number.isInteger(repositoryId) ||\n !Number.isInteger(installationId) ||\n !Number.isInteger(pullRequestNumber) ||\n pullRequestNumber < 1\n )\n return;\n const targetKey = `factory-pr-provenance:${repositoryId}:${pullRequestNumber}`;\n if (\n (await integrationStorage.subscriptions.listByTarget(targetKey)).some(row => row.orgId === input.binding.orgId)\n ) {\n return;\n }\n\n const { data } = await github\n .getInstallationOctokit(installationId)\n .pulls.get({ owner, repo, pull_number: pullRequestNumber });\n if (data.base.repo.id !== repositoryId || data.number !== pullRequestNumber || data.html_url !== url) return;\n\n await integrationStorage.subscriptions.create({\n orgId: input.binding.orgId,\n targetKey,\n threadId: input.binding.threadId,\n status: 'active',\n data: {\n kind: 'factory-pr-provenance',\n bindingId: input.binding.id,\n workItemId: input.item.id,\n repositoryId,\n pullRequestNumber,\n pullRequestUrl: url,\n assistantMessageId: input.assistantMessageId,\n toolCallId: input.toolCallId,\n },\n });\n } catch {\n return;\n }\n}\n"],"mappings":";;AA6BA,eAAsB,mCACpB,QACA,eACA,oBAKA,OACe;CACf,IAAI,MAAM,WAAW,aAAa,MAAM,KAAK,gBAAgB,SAAS,gBAAgB;CACtF,MAAM,MAAM,wBAAwB;EAClC,UAAU,MAAM;EAChB,OAAO,MAAM;EACb,QAAQ,MAAM;CAChB,CAAC;CACD,IAAI,CAAC,KAAK;CAEV,IAAI;EACF,MAAM,QAAQ,IAAI,MAAM,0DAA0D;EAClF,IAAI,CAAC,OAAO;EACZ,MAAM,iBAAiB,MAAM;EAC7B,IAAI;EACJ,IAAI;EACJ,KAAK,MAAM,cAAc,MAAM,cAAc,YAAY,KAAK;GAC5D,OAAO,MAAM,QAAQ;GACrB,kBAAkB,MAAM,QAAQ;EAClC,CAAC,GAAG;GACF,MAAM,eAAe,MAAM,cAAc,cAAc,IAAI;IACzD,OAAO,MAAM,QAAQ;IACrB,IAAI,WAAW;GACjB,CAAC;GACD,IAAI,CAAC,cAAc;GACnB,KAAK,MAAM,QAAQ,MAAM,cAAc,oBAAoB,KAAK;IAC9D,OAAO,MAAM,QAAQ;IACrB,cAAc,WAAW;GAC3B,CAAC,GAAG;IACF,MAAM,aAAa,MAAM,cAAc,aAAa,IAAI;KAAE,OAAO,MAAM,QAAQ;KAAO,IAAI,KAAK;IAAa,CAAC;IAC7G,IAAI,CAAC,cAAc,WAAW,KAAK,YAAY,MAAM,eAAe,YAAY,GAAG;IACnF,eAAe,OAAO,WAAW,UAAU;IAC3C,iBAAiB,OAAO,aAAa,UAAU;IAC/C;GACF;GACA,IAAI,iBAAiB,KAAA,GAAW;EAClC;EACA,MAAM,oBAAoB,OAAO,MAAM,EAAE;EACzC,MAAM,CAAC,OAAO,QAAQ,eAAe,MAAM,GAAG;EAC9C,IACE,CAAC,SACD,CAAC,QACD,iBAAiB,KAAA,KACjB,mBAAmB,KAAA,KACnB,CAAC,OAAO,UAAU,YAAY,KAC9B,CAAC,OAAO,UAAU,cAAc,KAChC,CAAC,OAAO,UAAU,iBAAiB,KACnC,oBAAoB,GAEpB;EACF,MAAM,YAAY,yBAAyB,aAAa,GAAG;EAC3D,KACG,MAAM,mBAAmB,cAAc,aAAa,SAAS,EAAA,CAAG,MAAK,QAAO,IAAI,UAAU,MAAM,QAAQ,KAAK,GAE9G;EAGF,MAAM,EAAE,SAAS,MAAM,OACpB,uBAAuB,cAAc,CAAC,CACtC,MAAM,IAAI;GAAE;GAAO;GAAM,aAAa;EAAkB,CAAC;EAC5D,IAAI,KAAK,KAAK,KAAK,OAAO,gBAAgB,KAAK,WAAW,qBAAqB,KAAK,aAAa,KAAK;EAEtG,MAAM,mBAAmB,cAAc,OAAO;GAC5C,OAAO,MAAM,QAAQ;GACrB;GACA,UAAU,MAAM,QAAQ;GACxB,QAAQ;GACR,MAAM;IACJ,MAAM;IACN,WAAW,MAAM,QAAQ;IACzB,YAAY,MAAM,KAAK;IACvB;IACA;IACA,gBAAgB;IAChB,oBAAoB,MAAM;IAC1B,YAAY,MAAM;GACpB;EACF,CAAC;
|
|
1
|
+
{"version":3,"file":"provenance.js","names":[],"sources":["../../../src/integrations/github/provenance.ts"],"sourcesContent":["import type { FactoryRuleJsonValue } from '../../rules/types.js';\nimport type { IntegrationStorageHandle } from '../../storage/domains/integrations/base.js';\nimport type { SourceControlStorageHandle } from '../../storage/domains/source-control/base.js';\nimport type { FactoryRunBindingRecord, WorkItemRow, WorkItemsStorage } from '../../storage/domains/work-items/base.js';\nimport type { GithubIntegration } from './integration.js';\nimport { parseCreatedPullRequest } from './session-subscriptions.js';\n\nexport interface RecordFactoryPullRequestProvenanceInput {\n binding: FactoryRunBindingRecord;\n item: WorkItemRow;\n assistantMessageId: string;\n toolCallId: string;\n toolName: string;\n toolInput: FactoryRuleJsonValue;\n toolResult: FactoryRuleJsonValue;\n status: 'success' | 'error';\n}\n\nexport interface FactoryPullRequestProvenanceData {\n kind: 'factory-pr-provenance';\n bindingId: string;\n workItemId: string;\n repositoryId: number;\n pullRequestNumber: number;\n pullRequestUrl: string;\n assistantMessageId: string;\n toolCallId: string;\n}\n\nexport async function resolveFactoryPullRequestParentWorkItemId(\n integrationStorage: IntegrationStorageHandle<\n Record<string, unknown>,\n Record<string, unknown>,\n FactoryPullRequestProvenanceData\n >,\n input: { orgId: string; repositoryId: number; pullRequestNumber: number },\n): Promise<string | null> {\n const targetKey = `factory-pr-provenance:${input.repositoryId}:${input.pullRequestNumber}`;\n const provenance = (await integrationStorage.subscriptions.listByTarget(targetKey, { status: 'active' })).find(\n row => row.orgId === input.orgId && row.data?.kind === 'factory-pr-provenance',\n );\n return provenance?.data?.workItemId ?? null;\n}\n\nexport async function recordFactoryPullRequestProvenance(\n github: GithubIntegration,\n sourceControl: SourceControlStorageHandle,\n integrationStorage: IntegrationStorageHandle<\n Record<string, unknown>,\n Record<string, unknown>,\n FactoryPullRequestProvenanceData\n >,\n workItems: WorkItemsStorage,\n input: RecordFactoryPullRequestProvenanceInput,\n): Promise<void> {\n if (input.status !== 'success' || input.item.externalSource?.type === 'pull-request') return;\n const url = parseCreatedPullRequest({\n toolName: input.toolName,\n input: input.toolInput,\n output: input.toolResult,\n });\n if (!url) return;\n\n try {\n const match = url.match(/^https:\\/\\/github\\.com\\/([^/]+\\/[^/]+)\\/pull\\/(\\d+)\\/?$/i);\n if (!match) return;\n const repositorySlug = match[1]!;\n let repositoryId: number | undefined;\n let installationId: number | undefined;\n for (const connection of await sourceControl.connections.list({\n orgId: input.binding.orgId,\n factoryProjectId: input.binding.factoryProjectId,\n })) {\n const installation = await sourceControl.installations.get({\n orgId: input.binding.orgId,\n id: connection.installationId,\n });\n if (!installation) continue;\n for (const link of await sourceControl.projectRepositories.list({\n orgId: input.binding.orgId,\n connectionId: connection.id,\n })) {\n const repository = await sourceControl.repositories.get({ orgId: input.binding.orgId, id: link.repositoryId });\n if (!repository || repository.slug.toLowerCase() !== repositorySlug.toLowerCase()) continue;\n repositoryId = Number(repository.externalId);\n installationId = Number(installation.externalId);\n break;\n }\n if (repositoryId !== undefined) break;\n }\n const pullRequestNumber = Number(match[2]);\n const [owner, repo] = repositorySlug.split('/');\n if (\n !owner ||\n !repo ||\n repositoryId === undefined ||\n installationId === undefined ||\n !Number.isInteger(repositoryId) ||\n !Number.isInteger(installationId) ||\n !Number.isInteger(pullRequestNumber) ||\n pullRequestNumber < 1\n )\n return;\n const targetKey = `factory-pr-provenance:${repositoryId}:${pullRequestNumber}`;\n if (\n (await integrationStorage.subscriptions.listByTarget(targetKey)).some(row => row.orgId === input.binding.orgId)\n ) {\n return;\n }\n\n const { data } = await github\n .getInstallationOctokit(installationId)\n .pulls.get({ owner, repo, pull_number: pullRequestNumber });\n if (data.base.repo.id !== repositoryId || data.number !== pullRequestNumber || data.html_url !== url) return;\n\n await integrationStorage.subscriptions.create({\n orgId: input.binding.orgId,\n targetKey,\n threadId: input.binding.threadId,\n status: 'active',\n data: {\n kind: 'factory-pr-provenance',\n bindingId: input.binding.id,\n workItemId: input.item.id,\n repositoryId,\n pullRequestNumber,\n pullRequestUrl: url,\n assistantMessageId: input.assistantMessageId,\n toolCallId: input.toolCallId,\n },\n });\n\n const reviewItem = (\n await workItems.list({\n orgId: input.binding.orgId,\n factoryProjectId: input.binding.factoryProjectId,\n })\n ).find(\n item =>\n item.externalSource?.integrationId === 'github' &&\n item.externalSource.type === 'pull-request' &&\n (item.externalSource.externalId === `github-pr:${pullRequestNumber}` ||\n item.externalSource.externalId === `github:${repositoryId}:pull-request:${pullRequestNumber}`),\n );\n if (reviewItem) {\n await workItems.setParentWorkItemIfMissing({\n orgId: input.binding.orgId,\n id: reviewItem.id,\n userId: 'factory-pr-provenance',\n parentWorkItemId: input.item.id,\n });\n }\n } catch {\n return;\n }\n}\n"],"mappings":"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"}
|
|
@@ -11,18 +11,21 @@
|
|
|
11
11
|
* so the SPA can cleanly hide all GitHub UI.
|
|
12
12
|
*/
|
|
13
13
|
import type { MountedMastraCode } from '@mastra/code-sdk';
|
|
14
|
-
import type { ApiRoute } from '@mastra/core/server';
|
|
14
|
+
import type { ApiRoute, IUserProvider } from '@mastra/core/server';
|
|
15
15
|
import type { FactoryStorage } from '@mastra/core/storage';
|
|
16
16
|
import type { RouteAuth } from '../../routes/route.js';
|
|
17
17
|
import type { SandboxFleet } from '../../sandbox/fleet.js';
|
|
18
18
|
import type { StateSigner } from '../../state-signing.js';
|
|
19
19
|
import type { AuditEmitter } from '../../storage/domains/audit/domain.js';
|
|
20
|
+
import type { MemorySettingsStorage } from '../../storage/domains/memory-settings/base.js';
|
|
20
21
|
import type { FactoryProjectsStorage } from '../../storage/domains/projects/base.js';
|
|
21
22
|
import type { GithubIntegration } from './integration.js';
|
|
22
23
|
import type { ParsedGithubWebhook } from './webhook.js';
|
|
23
24
|
export interface MountGithubRoutesOptions {
|
|
24
25
|
/** Host auth seam — resolves the signed-in user/tenant for each request. */
|
|
25
26
|
auth: RouteAuth;
|
|
27
|
+
/** Optional user directory used to resolve session-owner display profiles. */
|
|
28
|
+
users?: SessionOwnerUserProvider;
|
|
26
29
|
/**
|
|
27
30
|
* Sandbox fleet for per-project sandboxes. A fleet constructed without a
|
|
28
31
|
* machine config reports `enabled: false` and the sandbox-backed routes
|
|
@@ -52,6 +55,8 @@ export interface MountGithubRoutesOptions {
|
|
|
52
55
|
redirectUri?: string;
|
|
53
56
|
/** Controller used to route verified webhook notifications to exact subscribed sessions. */
|
|
54
57
|
controller?: MountedMastraCode['controller'];
|
|
58
|
+
/** Owner-scoped observational-memory settings — the source of the model that names a thread. */
|
|
59
|
+
memorySettings: Pick<MemorySettingsStorage, 'get'>;
|
|
55
60
|
/** Best-effort audit emission supplied by the factory-owned audit domain. */
|
|
56
61
|
emitAudit?: AuditEmitter['emit'];
|
|
57
62
|
/** Factory projects domain — resolves a project's default triage model. */
|
|
@@ -65,4 +70,6 @@ export interface MountGithubRoutesOptions {
|
|
|
65
70
|
* returns only the `status` route so the SPA can detect the disabled state.
|
|
66
71
|
*/
|
|
67
72
|
export declare function buildGithubRoutes(options: MountGithubRoutesOptions): ApiRoute[];
|
|
73
|
+
type SessionOwnerUserProvider = Pick<IUserProvider, 'getUser'> & Partial<Pick<IUserProvider, 'getUsers'>>;
|
|
74
|
+
export {};
|
|
68
75
|
//# sourceMappingURL=routes.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"routes.d.ts","sourceRoot":"","sources":["../../../src/integrations/github/routes.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;GAWG;AAGH,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,kBAAkB,CAAC;
|
|
1
|
+
{"version":3,"file":"routes.d.ts","sourceRoot":"","sources":["../../../src/integrations/github/routes.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;GAWG;AAGH,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,kBAAkB,CAAC;AAG1D,OAAO,KAAK,EAAE,QAAQ,EAAE,aAAa,EAAE,MAAM,qBAAqB,CAAC;AAGnE,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,sBAAsB,CAAC;AAG3D,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,uBAAuB,CAAC;AAGvD,OAAO,KAAK,EAAuD,YAAY,EAAE,MAAM,wBAAwB,CAAC;AAEhH,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,wBAAwB,CAAC;AAC1D,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,uCAAuC,CAAC;AAC1E,OAAO,KAAK,EAAE,qBAAqB,EAAE,MAAM,+CAA+C,CAAC;AAC3F,OAAO,KAAK,EAAE,sBAAsB,EAAE,MAAM,wCAAwC,CAAC;AASrF,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,kBAAkB,CAAC;AA2C1D,OAAO,KAAK,EAAE,mBAAmB,EAAE,MAAM,cAAc,CAAC;AAgBxD,MAAM,WAAW,wBAAwB;IACvC,4EAA4E;IAC5E,IAAI,EAAE,SAAS,CAAC;IAChB,8EAA8E;IAC9E,KAAK,CAAC,EAAE,wBAAwB,CAAC;IACjC;;;;OAIG;IACH,KAAK,EAAE,YAAY,CAAC;IACpB,yEAAyE;IACzE,OAAO,CAAC,EAAE,cAAc,CAAC;IACzB;;;;OAIG;IACH,MAAM,CAAC,EAAE,iBAAiB,CAAC;IAC3B;;;;OAIG;IACH,WAAW,CAAC,EAAE,WAAW,CAAC;IAC1B;;;OAGG;IACH,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,iFAAiF;IACjF,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,4FAA4F;IAC5F,UAAU,CAAC,EAAE,iBAAiB,CAAC,YAAY,CAAC,CAAC;IAC7C,gGAAgG;IAChG,cAAc,EAAE,IAAI,CAAC,qBAAqB,EAAE,KAAK,CAAC,CAAC;IACnD,6EAA6E;IAC7E,SAAS,CAAC,EAAE,YAAY,CAAC,MAAM,CAAC,CAAC;IACjC,2EAA2E;IAC3E,QAAQ,CAAC,EAAE,sBAAsB,CAAC;IAClC,iBAAiB,CAAC,EAAE,OAAO,qCAAqC,EAAE,4BAA4B,CAAC;IAC/F,+FAA+F;IAC/F,kBAAkB,CAAC,EAAE,CAAC,KAAK,EAAE,mBAAmB,KAAK,OAAO,CAAC,OAAO,CAAC,CAAC;CACvE;AA4ND;;;GAGG;AACH,wBAAgB,iBAAiB,CAAC,OAAO,EAAE,wBAAwB,GAAG,QAAQ,EAAE,CAqrB/E;AAkQD,KAAK,wBAAwB,GAAG,IAAI,CAAC,aAAa,EAAE,SAAS,CAAC,GAAG,OAAO,CAAC,IAAI,CAAC,aAAa,EAAE,UAAU,CAAC,CAAC,CAAC"}
|
|
@@ -3,12 +3,15 @@ import { listPullRequestSubscriptionsForThread, subscribeToPullRequest } from ".
|
|
|
3
3
|
import { SandboxBudgetError } from "../../sandbox/fleet.js";
|
|
4
4
|
import { MaterializeError, WorktreeError, commitAll, ensureProjectSandbox, isValidGitRef as isValidGitRef$1, materializeRepo, pushBranch, teardownProjectSandbox } from "./sandbox.js";
|
|
5
5
|
import { baseCheckpointIsStale } from "../../sandbox/base-checkpoint-triggers.js";
|
|
6
|
+
import { normalizeSessionTitle } from "../../session/session-title.js";
|
|
6
7
|
import { getGithubFeatureDiagnostics, isGithubFeatureEnabled } from "./config.js";
|
|
7
8
|
import { reclaimDeletedSessionSandbox } from "./sandbox-release.js";
|
|
8
9
|
import { handleGithubWebhook } from "./webhook.js";
|
|
9
10
|
import { registerApiRoute } from "@mastra/core/server";
|
|
10
11
|
import { randomUUID } from "crypto";
|
|
11
12
|
import { UniqueViolationError } from "@mastra/core/storage";
|
|
13
|
+
import { resolveModel } from "@mastra/code-sdk/agents/model";
|
|
14
|
+
import { RequestContext } from "@mastra/core/request-context";
|
|
12
15
|
import { streamSSE } from "hono/streaming";
|
|
13
16
|
//#region src/integrations/github/routes.ts
|
|
14
17
|
/**
|
|
@@ -24,7 +27,6 @@ import { streamSSE } from "hono/streaming";
|
|
|
24
27
|
* so the SPA can cleanly hide all GitHub UI.
|
|
25
28
|
*/
|
|
26
29
|
const sessionOperationLocks = /* @__PURE__ */ new Map();
|
|
27
|
-
const MAX_SESSION_TITLE_LENGTH = 80;
|
|
28
30
|
const USER_SESSION_BRANCH_PREFIX = "user/session-";
|
|
29
31
|
const UUID_PATTERN = /^[0-9a-f]{8}-[0-9a-f]{4}-[1-8][0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$/;
|
|
30
32
|
/**
|
|
@@ -69,9 +71,6 @@ function isValidGitRef(value) {
|
|
|
69
71
|
function isJsonObject(value) {
|
|
70
72
|
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
71
73
|
}
|
|
72
|
-
function normalizeSessionTitle(title) {
|
|
73
|
-
return [...title.replace(/\s+/g, " ").trim()].slice(0, MAX_SESSION_TITLE_LENGTH).join("").trimEnd() || null;
|
|
74
|
-
}
|
|
75
74
|
/**
|
|
76
75
|
* Resolve the org-scoped tenant for a GitHub request. GitHub project features
|
|
77
76
|
* are org-owned, so they require both a signed-in user and a WorkOS
|
|
@@ -108,6 +107,11 @@ function parseListPage(raw) {
|
|
|
108
107
|
const page = Number(raw);
|
|
109
108
|
return page >= 1 ? page : null;
|
|
110
109
|
}
|
|
110
|
+
function parseResourceNumber(raw) {
|
|
111
|
+
if (raw === void 0 || !/^\d{1,10}$/.test(raw)) return null;
|
|
112
|
+
const parsed = Number(raw);
|
|
113
|
+
return parsed > 0 ? parsed : null;
|
|
114
|
+
}
|
|
111
115
|
const VALID_ISSUE_LABEL_FILTERS = /* @__PURE__ */ new Set(["status: auto-triaged", "status: needs approval"]);
|
|
112
116
|
function parseIssueLabelFilter(raw) {
|
|
113
117
|
if (raw === void 0 || raw === "") return void 0;
|
|
@@ -208,7 +212,7 @@ async function ingestPolledEvents(events, ingestFactoryEvent) {
|
|
|
208
212
|
*/
|
|
209
213
|
function buildGithubRoutes(options) {
|
|
210
214
|
const routes = [];
|
|
211
|
-
const { auth, fleet, storage, github, stateSigner, controller, emitAudit, sessionRetirement } = options;
|
|
215
|
+
const { auth, users, fleet, storage, github, stateSigner, controller, memorySettings, emitAudit, sessionRetirement } = options;
|
|
212
216
|
const diagnostics = () => getGithubFeatureDiagnostics({
|
|
213
217
|
github,
|
|
214
218
|
auth,
|
|
@@ -531,6 +535,48 @@ function buildGithubRoutes(options) {
|
|
|
531
535
|
}
|
|
532
536
|
}
|
|
533
537
|
}));
|
|
538
|
+
routes.push(registerApiRoute("/web/github/projects/:id/issues/:number", {
|
|
539
|
+
method: "GET",
|
|
540
|
+
requiresAuth: false,
|
|
541
|
+
handler: async (c) => {
|
|
542
|
+
const loaded = await loadOrgProject({
|
|
543
|
+
github,
|
|
544
|
+
auth,
|
|
545
|
+
c: loose(c)
|
|
546
|
+
});
|
|
547
|
+
if ("response" in loaded) return loaded.response;
|
|
548
|
+
const number = parseResourceNumber(c.req.param("number"));
|
|
549
|
+
if (number === null) return c.json({ error: "invalid_number" }, 400);
|
|
550
|
+
try {
|
|
551
|
+
const detail = await github.intake.getIssue({
|
|
552
|
+
connection: {
|
|
553
|
+
type: "app-installation",
|
|
554
|
+
installationId: Number(loaded.project.installation.externalId)
|
|
555
|
+
},
|
|
556
|
+
sourceId: loaded.project.repository.slug,
|
|
557
|
+
issueId: String(number)
|
|
558
|
+
});
|
|
559
|
+
if (detail === null) return c.json({ error: "issue_not_found" }, 404);
|
|
560
|
+
return c.json({
|
|
561
|
+
number: Number(detail.id),
|
|
562
|
+
title: detail.title,
|
|
563
|
+
url: detail.url,
|
|
564
|
+
author: detail.author,
|
|
565
|
+
assignee: detail.assignee,
|
|
566
|
+
labels: detail.labels,
|
|
567
|
+
comments: detail.commentCount ?? 0,
|
|
568
|
+
createdAt: detail.createdAt,
|
|
569
|
+
updatedAt: detail.updatedAt,
|
|
570
|
+
description: detail.description
|
|
571
|
+
});
|
|
572
|
+
} catch (err) {
|
|
573
|
+
return c.json({
|
|
574
|
+
error: "github_fetch_failed",
|
|
575
|
+
message: err instanceof Error ? err.message : String(err)
|
|
576
|
+
}, 502);
|
|
577
|
+
}
|
|
578
|
+
}
|
|
579
|
+
}));
|
|
534
580
|
routes.push(registerApiRoute("/web/github/projects/:id/prs", {
|
|
535
581
|
method: "GET",
|
|
536
582
|
requiresAuth: false,
|
|
@@ -578,6 +624,49 @@ function buildGithubRoutes(options) {
|
|
|
578
624
|
}
|
|
579
625
|
}
|
|
580
626
|
}));
|
|
627
|
+
routes.push(registerApiRoute("/web/github/projects/:id/prs/:number", {
|
|
628
|
+
method: "GET",
|
|
629
|
+
requiresAuth: false,
|
|
630
|
+
handler: async (c) => {
|
|
631
|
+
const loaded = await loadOrgProject({
|
|
632
|
+
github,
|
|
633
|
+
auth,
|
|
634
|
+
c: loose(c)
|
|
635
|
+
});
|
|
636
|
+
if ("response" in loaded) return loaded.response;
|
|
637
|
+
const number = parseResourceNumber(c.req.param("number"));
|
|
638
|
+
if (number === null) return c.json({ error: "invalid_number" }, 400);
|
|
639
|
+
try {
|
|
640
|
+
const pr = await github.versionControl.getPullRequest({
|
|
641
|
+
connection: {
|
|
642
|
+
type: "app-installation",
|
|
643
|
+
installationId: Number(loaded.project.installation.externalId)
|
|
644
|
+
},
|
|
645
|
+
sourceId: loaded.project.repository.slug,
|
|
646
|
+
pullRequestId: String(number)
|
|
647
|
+
});
|
|
648
|
+
if (pr === null) return c.json({ error: "pull_request_not_found" }, 404);
|
|
649
|
+
return c.json({
|
|
650
|
+
number: Number(pr.id),
|
|
651
|
+
title: pr.title,
|
|
652
|
+
url: pr.url,
|
|
653
|
+
author: pr.author,
|
|
654
|
+
assignees: pr.assignees ?? [],
|
|
655
|
+
requestedReviewers: pr.requestedReviewers ?? [],
|
|
656
|
+
baseBranch: pr.baseBranch,
|
|
657
|
+
headBranch: pr.headBranch,
|
|
658
|
+
createdAt: pr.createdAt,
|
|
659
|
+
updatedAt: pr.updatedAt,
|
|
660
|
+
description: pr.body
|
|
661
|
+
});
|
|
662
|
+
} catch (err) {
|
|
663
|
+
return c.json({
|
|
664
|
+
error: "github_fetch_failed",
|
|
665
|
+
message: err instanceof Error ? err.message : String(err)
|
|
666
|
+
}, 502);
|
|
667
|
+
}
|
|
668
|
+
}
|
|
669
|
+
}));
|
|
581
670
|
routes.push(registerApiRoute("/web/github/projects/:id/settings", {
|
|
582
671
|
method: "GET",
|
|
583
672
|
requiresAuth: false,
|
|
@@ -686,8 +775,10 @@ function buildGithubRoutes(options) {
|
|
|
686
775
|
routes.push(...buildProjectGitRoutes({
|
|
687
776
|
github,
|
|
688
777
|
auth,
|
|
778
|
+
users,
|
|
689
779
|
fleet,
|
|
690
780
|
controller,
|
|
781
|
+
memorySettings,
|
|
691
782
|
emitAudit,
|
|
692
783
|
sessionRetirement
|
|
693
784
|
}));
|
|
@@ -894,7 +985,87 @@ async function loadOwnedProject(options) {
|
|
|
894
985
|
sandboxRow: await loadOrCreateSandboxRow(github, project, userId)
|
|
895
986
|
};
|
|
896
987
|
}
|
|
897
|
-
|
|
988
|
+
/**
|
|
989
|
+
* One naming per session at a time: a second caller joins the run already in
|
|
990
|
+
* flight instead of paying for another model call and racing its rename.
|
|
991
|
+
*/
|
|
992
|
+
function createSessionNaming() {
|
|
993
|
+
const inFlight = /* @__PURE__ */ new Map();
|
|
994
|
+
return (sessionId, run) => {
|
|
995
|
+
const pending = inFlight.get(sessionId);
|
|
996
|
+
if (pending) return pending;
|
|
997
|
+
const started = run().finally(() => inFlight.delete(sessionId));
|
|
998
|
+
inFlight.set(sessionId, started);
|
|
999
|
+
return started;
|
|
1000
|
+
};
|
|
1001
|
+
}
|
|
1002
|
+
/**
|
|
1003
|
+
* Name a thread with a stored model id. Resolution has to go through
|
|
1004
|
+
* mastracode's gateway: a bare id handed to core's model router looks for a
|
|
1005
|
+
* process env key instead of the caller's stored provider credentials.
|
|
1006
|
+
*/
|
|
1007
|
+
function titleModel(modelId) {
|
|
1008
|
+
return ({ requestContext }) => resolveModel(modelId, {
|
|
1009
|
+
remapForCodexOAuth: true,
|
|
1010
|
+
requestContext
|
|
1011
|
+
});
|
|
1012
|
+
}
|
|
1013
|
+
const MAX_SESSION_OWNER_PROFILES = 100;
|
|
1014
|
+
const MAX_SESSION_OWNER_PROFILE_CACHE_ENTRIES = 500;
|
|
1015
|
+
const SESSION_OWNER_PROFILE_TTL_MS = 5 * 6e4;
|
|
1016
|
+
function createSessionOwnerProfileResolver(users) {
|
|
1017
|
+
const cache = /* @__PURE__ */ new Map();
|
|
1018
|
+
const cacheProfile = (userId, profile, expiresAt) => {
|
|
1019
|
+
cache.delete(userId);
|
|
1020
|
+
cache.set(userId, {
|
|
1021
|
+
profile,
|
|
1022
|
+
expiresAt
|
|
1023
|
+
});
|
|
1024
|
+
if (cache.size > MAX_SESSION_OWNER_PROFILE_CACHE_ENTRIES) {
|
|
1025
|
+
const oldestUserId = cache.keys().next().value;
|
|
1026
|
+
if (oldestUserId !== void 0) cache.delete(oldestUserId);
|
|
1027
|
+
}
|
|
1028
|
+
};
|
|
1029
|
+
return async (userIds) => {
|
|
1030
|
+
if (!users) return /* @__PURE__ */ new Map();
|
|
1031
|
+
const requestedUserIds = [...new Set(userIds)].slice(0, MAX_SESSION_OWNER_PROFILES);
|
|
1032
|
+
const profiles = /* @__PURE__ */ new Map();
|
|
1033
|
+
const unresolvedUserIds = [];
|
|
1034
|
+
const now = Date.now();
|
|
1035
|
+
for (const userId of requestedUserIds) {
|
|
1036
|
+
const cached = cache.get(userId);
|
|
1037
|
+
if (!cached || cached.expiresAt <= now) {
|
|
1038
|
+
cache.delete(userId);
|
|
1039
|
+
unresolvedUserIds.push(userId);
|
|
1040
|
+
} else if (cached.profile) profiles.set(userId, cached.profile);
|
|
1041
|
+
}
|
|
1042
|
+
if (unresolvedUserIds.length === 0) return profiles;
|
|
1043
|
+
let resolvedUsers;
|
|
1044
|
+
if (users.getUsers) try {
|
|
1045
|
+
resolvedUsers = await users.getUsers(unresolvedUserIds);
|
|
1046
|
+
} catch (error) {
|
|
1047
|
+
console.warn("[GitHub Sessions] Bulk session owner profile lookup failed; falling back to individual lookups", { error: error instanceof Error ? error.message : String(error) });
|
|
1048
|
+
resolvedUsers = (await Promise.allSettled(unresolvedUserIds.map((userId) => users.getUser(userId)))).filter((result) => result.status === "fulfilled").map((result) => result.value);
|
|
1049
|
+
}
|
|
1050
|
+
else resolvedUsers = (await Promise.allSettled(unresolvedUserIds.map((userId) => users.getUser(userId)))).filter((result) => result.status === "fulfilled").map((result) => result.value);
|
|
1051
|
+
for (const user of resolvedUsers) {
|
|
1052
|
+
if (!user) continue;
|
|
1053
|
+
const name = user.name?.trim() || user.email?.trim();
|
|
1054
|
+
if (!name) continue;
|
|
1055
|
+
profiles.set(user.id, {
|
|
1056
|
+
id: user.id,
|
|
1057
|
+
name,
|
|
1058
|
+
...user.avatarUrl ? { avatarUrl: user.avatarUrl } : {}
|
|
1059
|
+
});
|
|
1060
|
+
}
|
|
1061
|
+
const expiresAt = now + SESSION_OWNER_PROFILE_TTL_MS;
|
|
1062
|
+
for (const userId of unresolvedUserIds) cacheProfile(userId, profiles.get(userId), expiresAt);
|
|
1063
|
+
return profiles;
|
|
1064
|
+
};
|
|
1065
|
+
}
|
|
1066
|
+
function buildProjectGitRoutes({ github, auth, users, fleet, controller, memorySettings, emitAudit, sessionRetirement }) {
|
|
1067
|
+
const nameSession = createSessionNaming();
|
|
1068
|
+
const resolveSessionOwnerProfiles = createSessionOwnerProfileResolver(users);
|
|
898
1069
|
return [
|
|
899
1070
|
registerApiRoute("/web/github/projects/:id/sessions", {
|
|
900
1071
|
method: "GET",
|
|
@@ -914,7 +1085,14 @@ function buildProjectGitRoutes({ github, auth, fleet, controller, emitAudit, ses
|
|
|
914
1085
|
projectRepositoryId: project.id,
|
|
915
1086
|
viewerUserId: userId
|
|
916
1087
|
});
|
|
917
|
-
|
|
1088
|
+
const owners = await resolveSessionOwnerProfiles(sessions.map((session) => session.userId));
|
|
1089
|
+
return c.json({ sessions: sessions.map((session) => {
|
|
1090
|
+
const owner = owners.get(session.userId);
|
|
1091
|
+
return {
|
|
1092
|
+
...session,
|
|
1093
|
+
...owner ? { owner } : {}
|
|
1094
|
+
};
|
|
1095
|
+
}) });
|
|
918
1096
|
}
|
|
919
1097
|
}),
|
|
920
1098
|
registerApiRoute("/web/github/projects/:id/sessions", {
|
|
@@ -1029,6 +1207,49 @@ function buildProjectGitRoutes({ github, auth, fleet, controller, emitAudit, ses
|
|
|
1029
1207
|
return c.json({ removed: true });
|
|
1030
1208
|
}
|
|
1031
1209
|
}),
|
|
1210
|
+
registerApiRoute("/web/user-sessions/:sessionId/title", {
|
|
1211
|
+
method: "POST",
|
|
1212
|
+
requiresAuth: false,
|
|
1213
|
+
handler: async (c) => {
|
|
1214
|
+
const resolved = await resolveOrgTenant(loose(c), auth);
|
|
1215
|
+
if ("response" in resolved) return resolved.response;
|
|
1216
|
+
const sessionId = c.req.param("sessionId");
|
|
1217
|
+
const row = await github.sourceControlStorage.sessions.getBySessionId(sessionId);
|
|
1218
|
+
if (!row || row.orgId !== resolved.tenant.orgId || row.userId !== resolved.tenant.userId) return c.json({ error: "Session not found" }, 404);
|
|
1219
|
+
if (!controller) return c.json({ error: "Sessions are not available on this server." }, 503);
|
|
1220
|
+
const thread = (await controller.queryThreads({ resourceId: sessionId })).sort((a, b) => b.updatedAt.getTime() - a.updatedAt.getTime())[0];
|
|
1221
|
+
if (!thread) return c.json({ error: "This session has no conversation to name yet." }, 409);
|
|
1222
|
+
const stored = await memorySettings.get({
|
|
1223
|
+
orgId: row.orgId,
|
|
1224
|
+
userId: row.userId
|
|
1225
|
+
});
|
|
1226
|
+
const requestContext = new RequestContext();
|
|
1227
|
+
requestContext.set("user", {
|
|
1228
|
+
workosId: row.userId,
|
|
1229
|
+
organizationId: row.orgId
|
|
1230
|
+
});
|
|
1231
|
+
try {
|
|
1232
|
+
const title = await nameSession(sessionId, async () => {
|
|
1233
|
+
const generated = await controller.generateThreadTitle({
|
|
1234
|
+
threadId: thread.id,
|
|
1235
|
+
resourceId: sessionId,
|
|
1236
|
+
requestContext,
|
|
1237
|
+
...stored?.observerModelId ? { model: titleModel(stored.observerModelId) } : {}
|
|
1238
|
+
});
|
|
1239
|
+
const named = generated ? normalizeSessionTitle(generated) : null;
|
|
1240
|
+
if (named) await github.sourceControlStorage.sessions.rename({
|
|
1241
|
+
sessionId,
|
|
1242
|
+
title: named
|
|
1243
|
+
});
|
|
1244
|
+
return named;
|
|
1245
|
+
});
|
|
1246
|
+
if (!title) return c.json({ error: "The model returned an empty title. Try again." }, 502);
|
|
1247
|
+
return c.json({ title });
|
|
1248
|
+
} catch (error) {
|
|
1249
|
+
return c.json({ error: error instanceof Error ? error.message : String(error) }, 500);
|
|
1250
|
+
}
|
|
1251
|
+
}
|
|
1252
|
+
}),
|
|
1032
1253
|
registerApiRoute("/web/github/projects/:id/commit", {
|
|
1033
1254
|
method: "POST",
|
|
1034
1255
|
requiresAuth: false,
|