@mastra/factory 0.1.0-alpha.3 → 0.1.0-alpha.5
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 +42 -0
- package/dist/capabilities/intake.d.ts +2 -0
- package/dist/capabilities/intake.d.ts.map +1 -1
- package/dist/capabilities/version-control.d.ts +6 -0
- package/dist/capabilities/version-control.d.ts.map +1 -1
- package/dist/factory.d.ts.map +1 -1
- package/dist/factory.js +912 -242
- package/dist/factory.js.map +1 -1
- package/dist/index.js +912 -242
- package/dist/index.js.map +1 -1
- package/dist/integrations/github/integration.d.ts +2 -2
- package/dist/integrations/github/integration.js +45 -12
- package/dist/integrations/github/integration.js.map +1 -1
- package/dist/integrations/github/provenance.js.map +1 -1
- package/dist/integrations/github/routes.js +12 -10
- package/dist/integrations/github/routes.js.map +1 -1
- package/dist/integrations/github/sandbox.d.ts +1 -0
- package/dist/integrations/github/sandbox.d.ts.map +1 -1
- package/dist/integrations/github/sandbox.js +2 -2
- package/dist/integrations/github/sandbox.js.map +1 -1
- package/dist/integrations/github/session-subscriptions.d.ts +3 -0
- package/dist/integrations/github/session-subscriptions.d.ts.map +1 -1
- package/dist/integrations/github/session-subscriptions.js +30 -0
- package/dist/integrations/github/session-subscriptions.js.map +1 -1
- package/dist/integrations/github/token-refresh.d.ts +6 -0
- package/dist/integrations/github/token-refresh.d.ts.map +1 -0
- package/dist/integrations/github/token-refresh.js +17 -0
- package/dist/integrations/github/token-refresh.js.map +1 -0
- package/dist/integrations/platform/api-client.d.ts +2 -0
- package/dist/integrations/platform/api-client.d.ts.map +1 -1
- package/dist/integrations/platform/api-client.js +7 -0
- package/dist/integrations/platform/api-client.js.map +1 -1
- package/dist/integrations/platform/github/event-worker.js.map +1 -1
- package/dist/integrations/platform/github/integration.d.ts.map +1 -1
- package/dist/integrations/platform/github/integration.js +173 -45
- package/dist/integrations/platform/github/integration.js.map +1 -1
- package/dist/integrations/platform/linear/integration.js +3 -0
- package/dist/integrations/platform/linear/integration.js.map +1 -1
- package/dist/routes/config.d.ts +16 -11
- package/dist/routes/config.d.ts.map +1 -1
- package/dist/routes/config.js +222 -109
- package/dist/routes/config.js.map +1 -1
- package/dist/routes/custom-provider-source.d.ts +52 -0
- package/dist/routes/custom-provider-source.d.ts.map +1 -0
- package/dist/routes/custom-provider-source.js +107 -0
- package/dist/routes/custom-provider-source.js.map +1 -0
- package/dist/routes/oauth.js +33 -1
- package/dist/routes/oauth.js.map +1 -1
- package/dist/routes/surface.d.ts +4 -0
- package/dist/routes/surface.d.ts.map +1 -1
- package/dist/routes/surface.js +266 -110
- package/dist/routes/surface.js.map +1 -1
- package/dist/sandbox/fleet.d.ts +3 -0
- package/dist/sandbox/fleet.d.ts.map +1 -1
- package/dist/sandbox/fleet.js +10 -3
- package/dist/sandbox/fleet.js.map +1 -1
- package/dist/spa-static.d.ts +4 -5
- package/dist/spa-static.d.ts.map +1 -1
- package/dist/spa-static.js +5 -2
- package/dist/spa-static.js.map +1 -1
- package/dist/storage/domains/custom-providers/base.d.ts +57 -0
- package/dist/storage/domains/custom-providers/base.d.ts.map +1 -0
- package/dist/storage/domains/custom-providers/base.js +150 -0
- package/dist/storage/domains/custom-providers/base.js.map +1 -0
- package/dist/storage/domains/memory-settings/base.d.ts +62 -0
- package/dist/storage/domains/memory-settings/base.d.ts.map +1 -0
- package/dist/storage/domains/memory-settings/base.js +111 -0
- package/dist/storage/domains/memory-settings/base.js.map +1 -0
- package/dist/workspace.d.ts.map +1 -1
- package/dist/workspace.js +17 -0
- package/dist/workspace.js.map +1 -1
- package/package.json +8 -7
package/dist/factory.js
CHANGED
|
@@ -11163,8 +11163,10 @@ function assertIdentifier(kind, name) {
|
|
|
11163
11163
|
}
|
|
11164
11164
|
}
|
|
11165
11165
|
function isUniqueViolation(error) {
|
|
11166
|
+
const code = typeof error === "object" && error !== null ? error.code : void 0;
|
|
11167
|
+
if (code === "SQLITE_CONSTRAINT_UNIQUE" || code === "SQLITE_CONSTRAINT_PRIMARYKEY") return true;
|
|
11166
11168
|
const message = error instanceof Error ? error.message : String(error);
|
|
11167
|
-
return message.includes("UNIQUE constraint failed") || message.includes("
|
|
11169
|
+
return message.includes("UNIQUE constraint failed") || message.includes("SQLITE_CONSTRAINT_UNIQUE") || message.includes("SQLITE_CONSTRAINT_PRIMARYKEY");
|
|
11168
11170
|
}
|
|
11169
11171
|
function primaryKeyOf(schema) {
|
|
11170
11172
|
const pks = Object.entries(schema.columns).filter(([, spec]) => spec.type === "uuid-pk" || spec.primaryKey);
|
|
@@ -24103,6 +24105,35 @@ ${unreflectedContent}` : bufferedReflection;
|
|
|
24103
24105
|
if (spec.default !== void 0) ddl += ` DEFAULT ${serializeDefault(spec.default)}`;
|
|
24104
24106
|
return ddl;
|
|
24105
24107
|
}
|
|
24108
|
+
/**
|
|
24109
|
+
* A table created by an older schema may still say NOT NULL on a column the
|
|
24110
|
+
* current schema declares nullable — inserts of null then fail on databases
|
|
24111
|
+
* that predate the change. SQLite has no `ALTER COLUMN DROP NOT NULL`, so
|
|
24112
|
+
* relaxation swaps in a table rebuilt from the current schema (create shadow
|
|
24113
|
+
* → copy → drop → rename) atomically.
|
|
24114
|
+
*/
|
|
24115
|
+
async #relaxDriftedNotNulls(schema) {
|
|
24116
|
+
const info = await this.#client.execute(`PRAGMA table_info("${schema.name}")`);
|
|
24117
|
+
const hasDrift = info.rows.some((row) => {
|
|
24118
|
+
const spec = schema.columns[String(row.name)];
|
|
24119
|
+
if (!spec || spec.type === "uuid-pk" || spec.primaryKey) return false;
|
|
24120
|
+
return spec.nullable === true && Number(row.notnull) === 1;
|
|
24121
|
+
});
|
|
24122
|
+
if (!hasDrift) return;
|
|
24123
|
+
const ddl = Object.entries(schema.columns).map(([name, spec]) => this.#columnDdl(name, spec));
|
|
24124
|
+
const shadow = `${schema.name}__nullable_rebuild`;
|
|
24125
|
+
const cols = Object.keys(schema.columns).map((name) => `"${name}"`).join(", ");
|
|
24126
|
+
await this.#client.batch(
|
|
24127
|
+
[
|
|
24128
|
+
`DROP TABLE IF EXISTS "${shadow}"`,
|
|
24129
|
+
`CREATE TABLE "${shadow}" (${ddl.join(", ")})`,
|
|
24130
|
+
`INSERT INTO "${shadow}" (${cols}) SELECT ${cols} FROM "${schema.name}"`,
|
|
24131
|
+
`DROP TABLE "${schema.name}"`,
|
|
24132
|
+
`ALTER TABLE "${shadow}" RENAME TO "${schema.name}"`
|
|
24133
|
+
],
|
|
24134
|
+
"write"
|
|
24135
|
+
);
|
|
24136
|
+
}
|
|
24106
24137
|
async #ensureCollection(schema) {
|
|
24107
24138
|
assertIdentifier("collection", schema.name);
|
|
24108
24139
|
primaryKeyOf(schema);
|
|
@@ -24114,6 +24145,7 @@ ${unreflectedContent}` : bufferedReflection;
|
|
|
24114
24145
|
if (existing.has(name)) continue;
|
|
24115
24146
|
await this.#client.execute(`ALTER TABLE "${schema.name}" ADD COLUMN ${this.#columnDdl(name, spec)}`);
|
|
24116
24147
|
}
|
|
24148
|
+
await this.#relaxDriftedNotNulls(schema);
|
|
24117
24149
|
for (const index of schema.uniqueIndexes ?? []) {
|
|
24118
24150
|
assertIdentifier("index", index.name);
|
|
24119
24151
|
index.columns.forEach((column) => assertIdentifier("column", column));
|
|
@@ -24798,6 +24830,19 @@ function retirePullRequestSubscription(id, status, storage) {
|
|
|
24798
24830
|
return storage.subscriptions.updateStatus(id, status);
|
|
24799
24831
|
}
|
|
24800
24832
|
|
|
24833
|
+
// src/integrations/github/token-refresh.ts
|
|
24834
|
+
var GITHUB_TOKEN_INJECTOR_CONTEXT_KEY = "factoryGithubTokenInjector";
|
|
24835
|
+
function registerGithubTokenInjector(requestContext, injector) {
|
|
24836
|
+
requestContext.set(GITHUB_TOKEN_INJECTOR_CONTEXT_KEY, injector);
|
|
24837
|
+
}
|
|
24838
|
+
function injectGithubToken(requestContext, token) {
|
|
24839
|
+
const injector = requestContext.get(GITHUB_TOKEN_INJECTOR_CONTEXT_KEY);
|
|
24840
|
+
if (!injector) {
|
|
24841
|
+
throw new Error("GitHub token refresh requires an active Factory sandbox workspace.");
|
|
24842
|
+
}
|
|
24843
|
+
injector(token);
|
|
24844
|
+
}
|
|
24845
|
+
|
|
24801
24846
|
// src/integrations/github/session-subscriptions.ts
|
|
24802
24847
|
function sessionUserId(user) {
|
|
24803
24848
|
return user?.workosId ?? user?.id;
|
|
@@ -24885,11 +24930,30 @@ async function unsubscribeCurrentSessionFromPullRequest(requestContext, pullRequ
|
|
|
24885
24930
|
await unsubscribeFromPullRequest(await subscriptionInput(target, number3), github.integrationStorage);
|
|
24886
24931
|
return number3;
|
|
24887
24932
|
}
|
|
24933
|
+
async function refreshGithubToken(requestContext, github) {
|
|
24934
|
+
const target = await resolveSessionTarget(requestContext, github);
|
|
24935
|
+
const access = await github.versionControl.getRepositoryAccess({
|
|
24936
|
+
orgId: target.orgId,
|
|
24937
|
+
repositoryId: target.repository.id
|
|
24938
|
+
});
|
|
24939
|
+
const token = access.authorization?.token;
|
|
24940
|
+
if (!token) throw new Error("Repository access did not include a bearer token for the Factory session.");
|
|
24941
|
+
injectGithubToken(requestContext, token);
|
|
24942
|
+
}
|
|
24888
24943
|
function createGithubSubscriptionTools(requestContext, github) {
|
|
24889
24944
|
const context = requestContext.get("controller");
|
|
24890
24945
|
const user = requestContext.get("user");
|
|
24891
24946
|
if (!context?.getState().projectRepositoryId || !sessionOrgId(user) || !sessionUserId(user)) return {};
|
|
24892
24947
|
return {
|
|
24948
|
+
github_refresh_token: createTool({
|
|
24949
|
+
id: "github_refresh_token",
|
|
24950
|
+
description: "Refresh GitHub CLI authentication in the active Factory sandbox. Use this after a gh command fails because authentication is expired, invalid, or missing. It installs a fresh GH_TOKEN for subsequent sandbox commands. After this tool succeeds, retry the failed gh command. Takes no arguments and never returns the token.",
|
|
24951
|
+
inputSchema: z.object({}),
|
|
24952
|
+
execute: async () => {
|
|
24953
|
+
await refreshGithubToken(requestContext, github);
|
|
24954
|
+
return { refreshed: true };
|
|
24955
|
+
}
|
|
24956
|
+
}),
|
|
24893
24957
|
github_subscribe_pr: createTool({
|
|
24894
24958
|
id: "github_subscribe_pr",
|
|
24895
24959
|
description: "Subscribe this thread to GitHub pull request activity. You usually do not need this tool: successful gh pr create commands subscribe automatically. Use it for an existing PR or to recover when automatic subscription did not occur. Closed or merged PRs are unsubscribed automatically. Accepts a PR number or canonical URL for the active project.",
|
|
@@ -25033,20 +25097,27 @@ var SandboxBudgetError = class extends Error {
|
|
|
25033
25097
|
max;
|
|
25034
25098
|
code = "sandbox-budget-exceeded";
|
|
25035
25099
|
};
|
|
25036
|
-
function toMaterializationSandbox(sandbox) {
|
|
25100
|
+
function toMaterializationSandbox(sandbox, initialEnvironment = {}) {
|
|
25037
25101
|
if (typeof sandbox.executeCommand !== "function") {
|
|
25038
25102
|
throw new Error(
|
|
25039
25103
|
`Sandbox provider '${sandbox.provider}' does not implement executeCommand() \u2014 cannot materialize repos.`
|
|
25040
25104
|
);
|
|
25041
25105
|
}
|
|
25042
25106
|
const lifecycle = sandbox;
|
|
25107
|
+
const environment = { ...initialEnvironment };
|
|
25043
25108
|
return {
|
|
25044
25109
|
id: sandbox.id,
|
|
25045
25110
|
start: async () => {
|
|
25046
25111
|
await (lifecycle._start ?? sandbox.start)?.call(sandbox);
|
|
25047
25112
|
},
|
|
25048
25113
|
getInfo: async () => await sandbox.getInfo?.() ?? {},
|
|
25049
|
-
executeCommand: (command, args, options) => sandbox.executeCommand(command, args,
|
|
25114
|
+
executeCommand: (command, args, options) => sandbox.executeCommand(command, args, {
|
|
25115
|
+
...options,
|
|
25116
|
+
env: { ...environment, ...options?.env }
|
|
25117
|
+
}),
|
|
25118
|
+
setEnvironmentVariable: (name, value) => {
|
|
25119
|
+
environment[name] = value;
|
|
25120
|
+
},
|
|
25050
25121
|
stop: async () => {
|
|
25051
25122
|
await (lifecycle._stop ?? sandbox.stop)?.call(sandbox);
|
|
25052
25123
|
}
|
|
@@ -25186,7 +25257,7 @@ var SandboxFleet = class {
|
|
|
25186
25257
|
...opts.idleTimeoutMinutes !== void 0 ? { idleTimeoutMinutes: opts.idleTimeoutMinutes } : {},
|
|
25187
25258
|
...opts.checkpointName ? { checkpointName: opts.checkpointName } : {}
|
|
25188
25259
|
});
|
|
25189
|
-
return toMaterializationSandbox(clone);
|
|
25260
|
+
return toMaterializationSandbox(clone, opts.env);
|
|
25190
25261
|
}
|
|
25191
25262
|
async ensureSandbox(store, envOrProgress, progressOrOptions, maybeOptions = {}) {
|
|
25192
25263
|
const env = typeof envOrProgress === "function" ? void 0 : envOrProgress;
|
|
@@ -25356,8 +25427,8 @@ function bindingStore(row, storage) {
|
|
|
25356
25427
|
};
|
|
25357
25428
|
}
|
|
25358
25429
|
async function ensureProjectSandbox(options) {
|
|
25359
|
-
const { fleet, row, storage, onProgress } = options;
|
|
25360
|
-
return fleet.ensureSandbox(bindingStore(row, storage), onProgress);
|
|
25430
|
+
const { fleet, row, storage, token, onProgress } = options;
|
|
25431
|
+
return fleet.ensureSandbox(bindingStore(row, storage), { GH_TOKEN: token }, onProgress);
|
|
25361
25432
|
}
|
|
25362
25433
|
async function teardownProjectSandbox(options) {
|
|
25363
25434
|
const { fleet, row, storage, sandbox } = options;
|
|
@@ -26623,13 +26694,6 @@ async function loadOrCreateSandboxRow(github, project, userId) {
|
|
|
26623
26694
|
async function prepareProject(options) {
|
|
26624
26695
|
const { github, fleet, project, userId, onProgress } = options;
|
|
26625
26696
|
const sandboxRow = await loadOrCreateSandboxRow(github, project, userId);
|
|
26626
|
-
const sandbox = await ensureProjectSandbox({
|
|
26627
|
-
fleet,
|
|
26628
|
-
row: sandboxRow,
|
|
26629
|
-
storage: github.sourceControlStorage.sandboxes,
|
|
26630
|
-
onProgress
|
|
26631
|
-
});
|
|
26632
|
-
const fresh = await github.sourceControlStorage.sandboxes.getById({ id: sandboxRow.id });
|
|
26633
26697
|
const access = await github.versionControl.getRepositoryAccess({
|
|
26634
26698
|
orgId: project.installation.orgId,
|
|
26635
26699
|
repositoryId: project.repository.id
|
|
@@ -26637,6 +26701,14 @@ async function prepareProject(options) {
|
|
|
26637
26701
|
if (!access.authorization) {
|
|
26638
26702
|
throw new MaterializeError("Repository access did not include a bearer token.", "clone-failed");
|
|
26639
26703
|
}
|
|
26704
|
+
const sandbox = await ensureProjectSandbox({
|
|
26705
|
+
fleet,
|
|
26706
|
+
row: sandboxRow,
|
|
26707
|
+
storage: github.sourceControlStorage.sandboxes,
|
|
26708
|
+
token: access.authorization.token,
|
|
26709
|
+
onProgress
|
|
26710
|
+
});
|
|
26711
|
+
const fresh = await github.sourceControlStorage.sandboxes.getById({ id: sandboxRow.id });
|
|
26640
26712
|
const finalRow = fresh ?? sandboxRow;
|
|
26641
26713
|
await materializeRepo({
|
|
26642
26714
|
row: finalRow,
|
|
@@ -26956,7 +27028,8 @@ function buildProjectGitRoutes({
|
|
|
26956
27028
|
baseBranch: base,
|
|
26957
27029
|
headBranch: head,
|
|
26958
27030
|
title,
|
|
26959
|
-
body: prBody
|
|
27031
|
+
body: prBody,
|
|
27032
|
+
actingUserId: userId
|
|
26960
27033
|
});
|
|
26961
27034
|
await emitAudit?.({
|
|
26962
27035
|
context: loose(c),
|
|
@@ -27142,6 +27215,9 @@ var PlatformApiClient = class {
|
|
|
27142
27215
|
accept: "application/json",
|
|
27143
27216
|
authorization: `Bearer ${this.#accessToken}`
|
|
27144
27217
|
};
|
|
27218
|
+
if (options?.actingUserId) {
|
|
27219
|
+
headers["x-acting-user-id"] = options.actingUserId;
|
|
27220
|
+
}
|
|
27145
27221
|
const timeoutSignal = AbortSignal.timeout(15e3);
|
|
27146
27222
|
const init = {
|
|
27147
27223
|
method,
|
|
@@ -27193,6 +27269,9 @@ function redact(message, accessToken) {
|
|
|
27193
27269
|
function logPlatformInfo(message, fields) {
|
|
27194
27270
|
writePlatformLog("info", message, fields);
|
|
27195
27271
|
}
|
|
27272
|
+
function logPlatformWarn(message, fields) {
|
|
27273
|
+
writePlatformLog("warn", message, fields);
|
|
27274
|
+
}
|
|
27196
27275
|
function logPlatformError(message, fields) {
|
|
27197
27276
|
writePlatformLog("error", message, fields);
|
|
27198
27277
|
}
|
|
@@ -27511,6 +27590,11 @@ function retryDelay(error, fallbackMs) {
|
|
|
27511
27590
|
// src/integrations/platform/github/integration.ts
|
|
27512
27591
|
var PAGE_SIZE = 30;
|
|
27513
27592
|
var API_PREFIX2 = "/v1/server";
|
|
27593
|
+
var REPOSITORY_TOKEN_PERMISSIONS = {
|
|
27594
|
+
contents: "write",
|
|
27595
|
+
issues: "write",
|
|
27596
|
+
pull_requests: "write"
|
|
27597
|
+
};
|
|
27514
27598
|
function loose2(c) {
|
|
27515
27599
|
return c;
|
|
27516
27600
|
}
|
|
@@ -27624,7 +27708,7 @@ var PlatformGithubIntegration = class {
|
|
|
27624
27708
|
return this.#listIssues(input.connection, sourceId, parsePositiveCursor(input.cursor), input.labels);
|
|
27625
27709
|
},
|
|
27626
27710
|
getIssue: (input) => this.#getIssue(input.connection, input.sourceId, input.issueId),
|
|
27627
|
-
createComment: (input) => this.#createIssueComment(input
|
|
27711
|
+
createComment: (input) => this.#createIssueComment(input)
|
|
27628
27712
|
};
|
|
27629
27713
|
versionControl = {
|
|
27630
27714
|
initialize: ({ storage }) => {
|
|
@@ -27663,7 +27747,7 @@ var PlatformGithubIntegration = class {
|
|
|
27663
27747
|
const token = await this.#client.request(
|
|
27664
27748
|
"POST",
|
|
27665
27749
|
`${API_PREFIX2}/github-app/installations/${installationId}/token`,
|
|
27666
|
-
{ repositories: [repositoryName], permissions:
|
|
27750
|
+
{ repositories: [repositoryName], permissions: REPOSITORY_TOKEN_PERMISSIONS }
|
|
27667
27751
|
);
|
|
27668
27752
|
return {
|
|
27669
27753
|
cloneUrl: `https://github.com/${repository.slug}.git`,
|
|
@@ -27727,6 +27811,7 @@ var PlatformGithubIntegration = class {
|
|
|
27727
27811
|
return [
|
|
27728
27812
|
this.#statusRoute(ctx),
|
|
27729
27813
|
this.#connectRoute(ctx),
|
|
27814
|
+
this.#connectUserRoute(ctx),
|
|
27730
27815
|
...buildGithubRoutes({
|
|
27731
27816
|
auth: ctx.auth,
|
|
27732
27817
|
fleet: ctx.fleet,
|
|
@@ -27758,11 +27843,16 @@ var PlatformGithubIntegration = class {
|
|
|
27758
27843
|
organizationRequired: true,
|
|
27759
27844
|
connected: false,
|
|
27760
27845
|
installations: [],
|
|
27846
|
+
userConnected: false,
|
|
27847
|
+
userGithubUsername: null,
|
|
27761
27848
|
reason: "organization_required",
|
|
27762
27849
|
diagnostics: this.diagnostics()
|
|
27763
27850
|
});
|
|
27764
27851
|
}
|
|
27765
|
-
const installations = await
|
|
27852
|
+
const [installations, userConnection] = await Promise.all([
|
|
27853
|
+
this.#syncInstallations(tenant.orgId, tenant.userId),
|
|
27854
|
+
this.#fetchUserConnection(tenant.userId)
|
|
27855
|
+
]);
|
|
27766
27856
|
return c.json({
|
|
27767
27857
|
enabled: true,
|
|
27768
27858
|
sandboxEnabled: ctx.fleet.enabled,
|
|
@@ -27772,6 +27862,8 @@ var PlatformGithubIntegration = class {
|
|
|
27772
27862
|
accountLogin: installation.accountName,
|
|
27773
27863
|
accountType: installation.accountType
|
|
27774
27864
|
})),
|
|
27865
|
+
userConnected: userConnection.connected,
|
|
27866
|
+
userGithubUsername: userConnection.githubUsername,
|
|
27775
27867
|
reason: installations.length > 0 ? "ready" : "not_connected",
|
|
27776
27868
|
diagnostics: this.diagnostics()
|
|
27777
27869
|
});
|
|
@@ -27806,6 +27898,55 @@ var PlatformGithubIntegration = class {
|
|
|
27806
27898
|
}
|
|
27807
27899
|
});
|
|
27808
27900
|
}
|
|
27901
|
+
#connectUserRoute(ctx) {
|
|
27902
|
+
return registerApiRoute3("/auth/github/connect-user", {
|
|
27903
|
+
method: "GET",
|
|
27904
|
+
requiresAuth: false,
|
|
27905
|
+
handler: async (c) => {
|
|
27906
|
+
await ctx.auth.ensureUser(loose2(c));
|
|
27907
|
+
const tenant = ctx.auth.tenant(loose2(c));
|
|
27908
|
+
if (!tenant?.orgId) return c.json({ error: "unauthorized" }, 401);
|
|
27909
|
+
const redirectTo = c.req.query("redirectTo") || c.req.query("return_to") || "/";
|
|
27910
|
+
const originator = routeBaseUrl(ctx, c.req.url);
|
|
27911
|
+
logPlatformInfo("Starting Platform GitHub user authorization flow", {
|
|
27912
|
+
orgId: tenant.orgId,
|
|
27913
|
+
redirectTo,
|
|
27914
|
+
originator
|
|
27915
|
+
});
|
|
27916
|
+
const query = new URLSearchParams({
|
|
27917
|
+
userId: tenant.userId,
|
|
27918
|
+
redirectTo,
|
|
27919
|
+
originator
|
|
27920
|
+
});
|
|
27921
|
+
const { url } = await this.#client.request(
|
|
27922
|
+
"GET",
|
|
27923
|
+
`${API_PREFIX2}/github-app/authenticate?${query}`
|
|
27924
|
+
);
|
|
27925
|
+
return c.redirect(url);
|
|
27926
|
+
}
|
|
27927
|
+
});
|
|
27928
|
+
}
|
|
27929
|
+
/**
|
|
27930
|
+
* Personal GitHub connection status for the acting user. Returns
|
|
27931
|
+
* not-connected when the platform predates the user-connection endpoint.
|
|
27932
|
+
*/
|
|
27933
|
+
async #fetchUserConnection(userId) {
|
|
27934
|
+
try {
|
|
27935
|
+
const connection = await this.#client.request(
|
|
27936
|
+
"GET",
|
|
27937
|
+
`${API_PREFIX2}/github-app/user-connection?${new URLSearchParams({ userId })}`
|
|
27938
|
+
);
|
|
27939
|
+
if (!connection.connected && connection.reason) {
|
|
27940
|
+
logPlatformWarn("Platform GitHub user connection verification failed", {
|
|
27941
|
+
userId,
|
|
27942
|
+
reason: connection.reason
|
|
27943
|
+
});
|
|
27944
|
+
}
|
|
27945
|
+
return connection;
|
|
27946
|
+
} catch {
|
|
27947
|
+
return { connected: false, githubUsername: null };
|
|
27948
|
+
}
|
|
27949
|
+
}
|
|
27809
27950
|
async #syncInstallations(orgId, userId) {
|
|
27810
27951
|
const result = await this.#client.request(
|
|
27811
27952
|
"GET",
|
|
@@ -27901,7 +28042,7 @@ var PlatformGithubIntegration = class {
|
|
|
27901
28042
|
const result = await this.#client.request(
|
|
27902
28043
|
"POST",
|
|
27903
28044
|
`${API_PREFIX2}/github-app/installations/${installationId}/token`,
|
|
27904
|
-
{ repositories: repositories.map((repository) => repository.name), permissions:
|
|
28045
|
+
{ repositories: repositories.map((repository) => repository.name), permissions: REPOSITORY_TOKEN_PERMISSIONS }
|
|
27905
28046
|
);
|
|
27906
28047
|
return result.token;
|
|
27907
28048
|
}
|
|
@@ -27956,15 +28097,16 @@ var PlatformGithubIntegration = class {
|
|
|
27956
28097
|
throw error;
|
|
27957
28098
|
}
|
|
27958
28099
|
}
|
|
27959
|
-
async #createIssueComment(
|
|
27960
|
-
requireGithubConnection(connection);
|
|
27961
|
-
const repository = requireSource(sourceId, "GitHub Intake requires a repository source.");
|
|
27962
|
-
const issueNumber = requirePositiveId(issueId, "issue");
|
|
28100
|
+
async #createIssueComment(input) {
|
|
28101
|
+
requireGithubConnection(input.connection);
|
|
28102
|
+
const repository = requireSource(input.sourceId, "GitHub Intake requires a repository source.");
|
|
28103
|
+
const issueNumber = requirePositiveId(input.issueId, "issue");
|
|
27963
28104
|
try {
|
|
27964
28105
|
const comment = await this.#client.request(
|
|
27965
28106
|
"POST",
|
|
27966
28107
|
repositoryPath(repository, `issues/${issueNumber}/comments`),
|
|
27967
|
-
{ body }
|
|
28108
|
+
{ body: input.body },
|
|
28109
|
+
{ actingUserId: input.actingUserId }
|
|
27968
28110
|
);
|
|
27969
28111
|
return { id: String(comment.id), url: comment.htmlUrl };
|
|
27970
28112
|
} catch (error) {
|
|
@@ -28001,29 +28143,40 @@ var PlatformGithubIntegration = class {
|
|
|
28001
28143
|
}
|
|
28002
28144
|
async #createPullRequest(input) {
|
|
28003
28145
|
requireGithubConnection(input.connection);
|
|
28004
|
-
const result = await this.#client.request(
|
|
28005
|
-
|
|
28006
|
-
|
|
28007
|
-
|
|
28008
|
-
|
|
28009
|
-
|
|
28010
|
-
|
|
28146
|
+
const result = await this.#client.request(
|
|
28147
|
+
"POST",
|
|
28148
|
+
repositoryPath(input.sourceId, "pulls"),
|
|
28149
|
+
{
|
|
28150
|
+
head: input.headBranch,
|
|
28151
|
+
base: input.baseBranch,
|
|
28152
|
+
title: input.title,
|
|
28153
|
+
body: input.body,
|
|
28154
|
+
draft: input.draft
|
|
28155
|
+
},
|
|
28156
|
+
{ actingUserId: input.actingUserId }
|
|
28157
|
+
);
|
|
28011
28158
|
return parsePullRequest2(result);
|
|
28012
28159
|
}
|
|
28013
28160
|
async #updatePullRequest(input) {
|
|
28014
|
-
const result = await this.#client.request(
|
|
28015
|
-
|
|
28016
|
-
|
|
28017
|
-
|
|
28018
|
-
|
|
28019
|
-
|
|
28161
|
+
const result = await this.#client.request(
|
|
28162
|
+
"PATCH",
|
|
28163
|
+
pullRequestPath(input, input.pullRequestId),
|
|
28164
|
+
{
|
|
28165
|
+
title: input.title,
|
|
28166
|
+
body: input.body === null ? "" : input.body,
|
|
28167
|
+
base: input.baseBranch,
|
|
28168
|
+
state: input.state
|
|
28169
|
+
},
|
|
28170
|
+
{ actingUserId: input.actingUserId }
|
|
28171
|
+
);
|
|
28020
28172
|
return parsePullRequest2(result);
|
|
28021
28173
|
}
|
|
28022
28174
|
#mergePullRequest(input) {
|
|
28023
28175
|
return this.#client.request(
|
|
28024
28176
|
"PUT",
|
|
28025
28177
|
`${pullRequestPath(input, input.pullRequestId)}/merge`,
|
|
28026
|
-
{ commitTitle: input.commitTitle, commitMessage: input.commitMessage, method: input.method }
|
|
28178
|
+
{ commitTitle: input.commitTitle, commitMessage: input.commitMessage, method: input.method },
|
|
28179
|
+
{ actingUserId: input.actingUserId }
|
|
28027
28180
|
);
|
|
28028
28181
|
}
|
|
28029
28182
|
async #listComments(input) {
|
|
@@ -28041,7 +28194,8 @@ var PlatformGithubIntegration = class {
|
|
|
28041
28194
|
const comment = await this.#client.request(
|
|
28042
28195
|
"POST",
|
|
28043
28196
|
repositoryPath(input.sourceId, `issues/${requirePositiveId(input.pullRequestId, "pull request")}/comments`),
|
|
28044
|
-
{ body: input.body }
|
|
28197
|
+
{ body: input.body },
|
|
28198
|
+
{ actingUserId: input.actingUserId }
|
|
28045
28199
|
);
|
|
28046
28200
|
return parseComment(comment);
|
|
28047
28201
|
}
|
|
@@ -28050,7 +28204,8 @@ var PlatformGithubIntegration = class {
|
|
|
28050
28204
|
const comment = await this.#client.request(
|
|
28051
28205
|
"PATCH",
|
|
28052
28206
|
repositoryPath(input.sourceId, `issues/comments/${requirePositiveId(input.commentId, "comment")}`),
|
|
28053
|
-
{ body: input.body }
|
|
28207
|
+
{ body: input.body },
|
|
28208
|
+
{ actingUserId: input.actingUserId }
|
|
28054
28209
|
);
|
|
28055
28210
|
return parseComment(comment);
|
|
28056
28211
|
}
|
|
@@ -28058,7 +28213,9 @@ var PlatformGithubIntegration = class {
|
|
|
28058
28213
|
requireGithubConnection(input.connection);
|
|
28059
28214
|
await this.#client.request(
|
|
28060
28215
|
"DELETE",
|
|
28061
|
-
repositoryPath(input.sourceId, `issues/comments/${requirePositiveId(input.commentId, "comment")}`)
|
|
28216
|
+
repositoryPath(input.sourceId, `issues/comments/${requirePositiveId(input.commentId, "comment")}`),
|
|
28217
|
+
void 0,
|
|
28218
|
+
{ actingUserId: input.actingUserId }
|
|
28062
28219
|
);
|
|
28063
28220
|
}
|
|
28064
28221
|
async #listReviews(input) {
|
|
@@ -28089,7 +28246,8 @@ var PlatformGithubIntegration = class {
|
|
|
28089
28246
|
const review = await this.#client.request(
|
|
28090
28247
|
"POST",
|
|
28091
28248
|
`${pullRequestPath(input, input.pullRequestId)}/reviews`,
|
|
28092
|
-
{ body: input.body, commitId: input.commitId, event: input.event ? reviewEvent(input.event) : void 0 }
|
|
28249
|
+
{ body: input.body, commitId: input.commitId, event: input.event ? reviewEvent(input.event) : void 0 },
|
|
28250
|
+
{ actingUserId: input.actingUserId }
|
|
28093
28251
|
);
|
|
28094
28252
|
return parseReview(review);
|
|
28095
28253
|
}
|
|
@@ -28097,7 +28255,8 @@ var PlatformGithubIntegration = class {
|
|
|
28097
28255
|
const review = await this.#client.request(
|
|
28098
28256
|
"PUT",
|
|
28099
28257
|
`${pullRequestPath(input, input.pullRequestId)}/reviews/${requirePositiveId(input.reviewId, "review")}`,
|
|
28100
|
-
{ body: input.body }
|
|
28258
|
+
{ body: input.body },
|
|
28259
|
+
{ actingUserId: input.actingUserId }
|
|
28101
28260
|
);
|
|
28102
28261
|
return parseReview(review);
|
|
28103
28262
|
}
|
|
@@ -28105,7 +28264,8 @@ var PlatformGithubIntegration = class {
|
|
|
28105
28264
|
const review = await this.#client.request(
|
|
28106
28265
|
"POST",
|
|
28107
28266
|
`${pullRequestPath(input, input.pullRequestId)}/reviews/${requirePositiveId(input.reviewId, "review")}/events`,
|
|
28108
|
-
{ body: input.body, event: reviewEvent(input.event) }
|
|
28267
|
+
{ body: input.body, event: reviewEvent(input.event) },
|
|
28268
|
+
{ actingUserId: input.actingUserId }
|
|
28109
28269
|
);
|
|
28110
28270
|
return parseReview(review);
|
|
28111
28271
|
}
|
|
@@ -28113,14 +28273,17 @@ var PlatformGithubIntegration = class {
|
|
|
28113
28273
|
const review = await this.#client.request(
|
|
28114
28274
|
"PUT",
|
|
28115
28275
|
`${pullRequestPath(input, input.pullRequestId)}/reviews/${requirePositiveId(input.reviewId, "review")}/dismissals`,
|
|
28116
|
-
{ message: input.message }
|
|
28276
|
+
{ message: input.message },
|
|
28277
|
+
{ actingUserId: input.actingUserId }
|
|
28117
28278
|
);
|
|
28118
28279
|
return parseReview(review);
|
|
28119
28280
|
}
|
|
28120
28281
|
async #deletePendingReview(input) {
|
|
28121
28282
|
await this.#client.request(
|
|
28122
28283
|
"DELETE",
|
|
28123
|
-
`${pullRequestPath(input, input.pullRequestId)}/reviews/${requirePositiveId(input.reviewId, "review")}
|
|
28284
|
+
`${pullRequestPath(input, input.pullRequestId)}/reviews/${requirePositiveId(input.reviewId, "review")}`,
|
|
28285
|
+
void 0,
|
|
28286
|
+
{ actingUserId: input.actingUserId }
|
|
28124
28287
|
);
|
|
28125
28288
|
}
|
|
28126
28289
|
async #listReviewComments(input) {
|
|
@@ -28156,7 +28319,8 @@ var PlatformGithubIntegration = class {
|
|
|
28156
28319
|
await this.#client.request(
|
|
28157
28320
|
"POST",
|
|
28158
28321
|
`${pullRequestPath(input, input.pullRequestId)}/comments`,
|
|
28159
|
-
body
|
|
28322
|
+
body,
|
|
28323
|
+
{ actingUserId: input.actingUserId }
|
|
28160
28324
|
)
|
|
28161
28325
|
);
|
|
28162
28326
|
}
|
|
@@ -28166,7 +28330,8 @@ var PlatformGithubIntegration = class {
|
|
|
28166
28330
|
await this.#client.request(
|
|
28167
28331
|
"PATCH",
|
|
28168
28332
|
repositoryPath(input.sourceId, `pulls/comments/${requirePositiveId(input.commentId, "review comment")}`),
|
|
28169
|
-
{ body: input.body }
|
|
28333
|
+
{ body: input.body },
|
|
28334
|
+
{ actingUserId: input.actingUserId }
|
|
28170
28335
|
)
|
|
28171
28336
|
);
|
|
28172
28337
|
}
|
|
@@ -28174,7 +28339,9 @@ var PlatformGithubIntegration = class {
|
|
|
28174
28339
|
requireGithubConnection(input.connection);
|
|
28175
28340
|
await this.#client.request(
|
|
28176
28341
|
"DELETE",
|
|
28177
|
-
repositoryPath(input.sourceId, `pulls/comments/${requirePositiveId(input.commentId, "review comment")}`)
|
|
28342
|
+
repositoryPath(input.sourceId, `pulls/comments/${requirePositiveId(input.commentId, "review comment")}`),
|
|
28343
|
+
void 0,
|
|
28344
|
+
{ actingUserId: input.actingUserId }
|
|
28178
28345
|
);
|
|
28179
28346
|
}
|
|
28180
28347
|
#requestedReviewers(method, input) {
|
|
@@ -28182,7 +28349,8 @@ var PlatformGithubIntegration = class {
|
|
|
28182
28349
|
return this.#client.request(
|
|
28183
28350
|
method,
|
|
28184
28351
|
`${pullRequestPath(input, input.pullRequestId)}/requested-reviewers`,
|
|
28185
|
-
method === "GET" ? void 0 : { users: input.users, teams: input.teams }
|
|
28352
|
+
method === "GET" ? void 0 : { users: input.users, teams: input.teams },
|
|
28353
|
+
method === "GET" ? void 0 : { actingUserId: input.actingUserId }
|
|
28186
28354
|
);
|
|
28187
28355
|
}
|
|
28188
28356
|
};
|
|
@@ -29076,6 +29244,158 @@ function isNotFound2(error) {
|
|
|
29076
29244
|
return error instanceof PlatformApiError && error.status === 404;
|
|
29077
29245
|
}
|
|
29078
29246
|
|
|
29247
|
+
// src/routes/custom-provider-source.ts
|
|
29248
|
+
import { setCustomProvidersSource } from "@mastra/code-sdk/agents/custom-provider-source";
|
|
29249
|
+
|
|
29250
|
+
// src/routes/provider-credentials.ts
|
|
29251
|
+
function getAuthProviderId(provider) {
|
|
29252
|
+
return provider === "openai" ? "openai-codex" : provider;
|
|
29253
|
+
}
|
|
29254
|
+
var WEB_OAUTH_FLOW_KINDS = {
|
|
29255
|
+
anthropic: "paste-code",
|
|
29256
|
+
openai: "device-code",
|
|
29257
|
+
"github-copilot": "device-code",
|
|
29258
|
+
xai: "device-code"
|
|
29259
|
+
};
|
|
29260
|
+
async function getTenantCredentialsStorage(credentials) {
|
|
29261
|
+
if (!credentials) return void 0;
|
|
29262
|
+
try {
|
|
29263
|
+
await credentials.ensureReady();
|
|
29264
|
+
} catch {
|
|
29265
|
+
return void 0;
|
|
29266
|
+
}
|
|
29267
|
+
return credentials;
|
|
29268
|
+
}
|
|
29269
|
+
function tenantOrgId(tenant) {
|
|
29270
|
+
return tenant.orgId ?? `user:${tenant.userId}`;
|
|
29271
|
+
}
|
|
29272
|
+
async function resolveCredentialContext({
|
|
29273
|
+
c,
|
|
29274
|
+
auth,
|
|
29275
|
+
credentials
|
|
29276
|
+
}) {
|
|
29277
|
+
await auth.ensureUser(c);
|
|
29278
|
+
const tenant = auth.tenant(c);
|
|
29279
|
+
if (!tenant) {
|
|
29280
|
+
if (auth.enabled()) return { response: c.json({ error: "unauthorized" }, 401) };
|
|
29281
|
+
return { mode: "local" };
|
|
29282
|
+
}
|
|
29283
|
+
const storage = await getTenantCredentialsStorage(credentials);
|
|
29284
|
+
if (!storage) {
|
|
29285
|
+
return {
|
|
29286
|
+
response: c.json(
|
|
29287
|
+
{
|
|
29288
|
+
error: "credentials_unavailable",
|
|
29289
|
+
message: "Tenant credential storage is unavailable \u2014 the app database is not configured or failed to start."
|
|
29290
|
+
},
|
|
29291
|
+
503
|
|
29292
|
+
)
|
|
29293
|
+
};
|
|
29294
|
+
}
|
|
29295
|
+
return { mode: "tenant", storage, orgId: tenantOrgId(tenant), userId: tenant.userId };
|
|
29296
|
+
}
|
|
29297
|
+
async function listTenantCredentialsForRequest({
|
|
29298
|
+
c,
|
|
29299
|
+
auth,
|
|
29300
|
+
credentials
|
|
29301
|
+
}) {
|
|
29302
|
+
await auth.ensureUser(c);
|
|
29303
|
+
const tenant = auth.tenant(c);
|
|
29304
|
+
if (!tenant) return auth.enabled() ? [] : void 0;
|
|
29305
|
+
const storage = await getTenantCredentialsStorage(credentials);
|
|
29306
|
+
if (!storage) return [];
|
|
29307
|
+
return storage.listCredentials(tenantOrgId(tenant), tenant.userId);
|
|
29308
|
+
}
|
|
29309
|
+
|
|
29310
|
+
// src/routes/custom-provider-source.ts
|
|
29311
|
+
var SNAPSHOT_TTL_MS = 15e3;
|
|
29312
|
+
var MAX_CACHED_ORGS = 1e3;
|
|
29313
|
+
var LOCAL_ORG = "local";
|
|
29314
|
+
var OrgCustomProvidersSnapshot = class {
|
|
29315
|
+
#orgId;
|
|
29316
|
+
#storage;
|
|
29317
|
+
#snapshot = [];
|
|
29318
|
+
#fetchedAt = 0;
|
|
29319
|
+
#hydrating;
|
|
29320
|
+
constructor(orgId, storage) {
|
|
29321
|
+
this.#orgId = orgId;
|
|
29322
|
+
this.#storage = storage;
|
|
29323
|
+
}
|
|
29324
|
+
/** Hydrate the snapshot when stale; coalesces concurrent callers. */
|
|
29325
|
+
async ensureFresh(now = Date.now()) {
|
|
29326
|
+
if (now - this.#fetchedAt < SNAPSHOT_TTL_MS) return;
|
|
29327
|
+
this.#hydrating ??= this.#hydrate().finally(() => {
|
|
29328
|
+
this.#hydrating = void 0;
|
|
29329
|
+
});
|
|
29330
|
+
await this.#hydrating;
|
|
29331
|
+
}
|
|
29332
|
+
async #hydrate() {
|
|
29333
|
+
await this.#storage.ensureReady();
|
|
29334
|
+
const records = await this.#storage.list({ orgId: this.#orgId });
|
|
29335
|
+
this.#snapshot = records.map((record) => ({
|
|
29336
|
+
name: record.name,
|
|
29337
|
+
url: record.url,
|
|
29338
|
+
apiKey: record.apiKey ?? void 0,
|
|
29339
|
+
models: record.models
|
|
29340
|
+
}));
|
|
29341
|
+
this.#fetchedAt = Date.now();
|
|
29342
|
+
}
|
|
29343
|
+
/** Sync by contract; kicks a background re-hydrate when the snapshot is stale. */
|
|
29344
|
+
get() {
|
|
29345
|
+
if (Date.now() - this.#fetchedAt >= SNAPSHOT_TTL_MS) {
|
|
29346
|
+
void this.ensureFresh().catch(() => {
|
|
29347
|
+
});
|
|
29348
|
+
}
|
|
29349
|
+
return this.#snapshot;
|
|
29350
|
+
}
|
|
29351
|
+
};
|
|
29352
|
+
var orgSnapshots = /* @__PURE__ */ new Map();
|
|
29353
|
+
function snapshotFor(orgId, storage) {
|
|
29354
|
+
let snapshot = orgSnapshots.get(orgId);
|
|
29355
|
+
if (!snapshot) {
|
|
29356
|
+
if (orgSnapshots.size >= MAX_CACHED_ORGS) {
|
|
29357
|
+
const oldest = orgSnapshots.keys().next().value;
|
|
29358
|
+
if (oldest !== void 0) orgSnapshots.delete(oldest);
|
|
29359
|
+
}
|
|
29360
|
+
snapshot = new OrgCustomProvidersSnapshot(orgId, storage);
|
|
29361
|
+
orgSnapshots.set(orgId, snapshot);
|
|
29362
|
+
}
|
|
29363
|
+
return snapshot;
|
|
29364
|
+
}
|
|
29365
|
+
function orgForTenant(tenant, authEnabled) {
|
|
29366
|
+
if (!authEnabled) return LOCAL_ORG;
|
|
29367
|
+
return tenant ? tenantOrgId(tenant) : void 0;
|
|
29368
|
+
}
|
|
29369
|
+
function registerCustomProvidersSource({
|
|
29370
|
+
storage,
|
|
29371
|
+
authEnabled
|
|
29372
|
+
}) {
|
|
29373
|
+
setCustomProvidersSource((tenant) => {
|
|
29374
|
+
const orgId = orgForTenant(tenant, authEnabled);
|
|
29375
|
+
if (!orgId) return [];
|
|
29376
|
+
return snapshotFor(orgId, storage).get();
|
|
29377
|
+
});
|
|
29378
|
+
}
|
|
29379
|
+
function invalidateCustomProvidersSnapshots(tenant) {
|
|
29380
|
+
orgSnapshots.delete(tenant.orgId);
|
|
29381
|
+
}
|
|
29382
|
+
function createCustomProvidersPrimer({
|
|
29383
|
+
auth,
|
|
29384
|
+
storage,
|
|
29385
|
+
authEnabled
|
|
29386
|
+
}) {
|
|
29387
|
+
return async (c, next) => {
|
|
29388
|
+
const orgId = orgForTenant(auth.tenant(c), authEnabled);
|
|
29389
|
+
if (orgId) {
|
|
29390
|
+
try {
|
|
29391
|
+
await snapshotFor(orgId, storage).ensureFresh();
|
|
29392
|
+
} catch {
|
|
29393
|
+
}
|
|
29394
|
+
}
|
|
29395
|
+
await next();
|
|
29396
|
+
};
|
|
29397
|
+
}
|
|
29398
|
+
|
|
29079
29399
|
// src/routes/projects.ts
|
|
29080
29400
|
import { registerApiRoute as registerApiRoute6 } from "@mastra/core/server";
|
|
29081
29401
|
|
|
@@ -30503,82 +30823,10 @@ var FactoryTransitionService = class {
|
|
|
30503
30823
|
};
|
|
30504
30824
|
|
|
30505
30825
|
// src/routes/config.ts
|
|
30506
|
-
import {
|
|
30507
|
-
import {
|
|
30508
|
-
removeCustomProviderFromSettings,
|
|
30509
|
-
upsertCustomProviderInSettings
|
|
30510
|
-
} from "@mastra/code-sdk/onboarding/custom-providers";
|
|
30511
|
-
import { applyOmRoleOverride, persistOmObserveAttachments } from "@mastra/code-sdk/onboarding/om-settings";
|
|
30826
|
+
import { DEFAULT_OM_MODEL_ID } from "@mastra/code-sdk/constants";
|
|
30512
30827
|
import { getAvailableModePacks } from "@mastra/code-sdk/onboarding/packs";
|
|
30513
|
-
import {
|
|
30514
|
-
getCustomProviderId,
|
|
30515
|
-
loadSettings,
|
|
30516
|
-
saveSettings,
|
|
30517
|
-
THREAD_ACTIVE_MODEL_PACK_ID_KEY
|
|
30518
|
-
} from "@mastra/code-sdk/onboarding/settings";
|
|
30828
|
+
import { getCustomProviderId, THREAD_ACTIVE_MODEL_PACK_ID_KEY } from "@mastra/code-sdk/onboarding/settings";
|
|
30519
30829
|
import { registerApiRoute as registerApiRoute7 } from "@mastra/core/server";
|
|
30520
|
-
|
|
30521
|
-
// src/routes/provider-credentials.ts
|
|
30522
|
-
function getAuthProviderId(provider) {
|
|
30523
|
-
return provider === "openai" ? "openai-codex" : provider;
|
|
30524
|
-
}
|
|
30525
|
-
var WEB_OAUTH_FLOW_KINDS = {
|
|
30526
|
-
anthropic: "paste-code",
|
|
30527
|
-
openai: "device-code",
|
|
30528
|
-
"github-copilot": "device-code",
|
|
30529
|
-
xai: "device-code"
|
|
30530
|
-
};
|
|
30531
|
-
async function getTenantCredentialsStorage(credentials) {
|
|
30532
|
-
if (!credentials) return void 0;
|
|
30533
|
-
try {
|
|
30534
|
-
await credentials.ensureReady();
|
|
30535
|
-
} catch {
|
|
30536
|
-
return void 0;
|
|
30537
|
-
}
|
|
30538
|
-
return credentials;
|
|
30539
|
-
}
|
|
30540
|
-
function tenantOrgId(tenant) {
|
|
30541
|
-
return tenant.orgId ?? `user:${tenant.userId}`;
|
|
30542
|
-
}
|
|
30543
|
-
async function resolveCredentialContext({
|
|
30544
|
-
c,
|
|
30545
|
-
auth,
|
|
30546
|
-
credentials
|
|
30547
|
-
}) {
|
|
30548
|
-
await auth.ensureUser(c);
|
|
30549
|
-
const tenant = auth.tenant(c);
|
|
30550
|
-
if (!tenant) {
|
|
30551
|
-
if (auth.enabled()) return { response: c.json({ error: "unauthorized" }, 401) };
|
|
30552
|
-
return { mode: "local" };
|
|
30553
|
-
}
|
|
30554
|
-
const storage = await getTenantCredentialsStorage(credentials);
|
|
30555
|
-
if (!storage) {
|
|
30556
|
-
return {
|
|
30557
|
-
response: c.json(
|
|
30558
|
-
{
|
|
30559
|
-
error: "credentials_unavailable",
|
|
30560
|
-
message: "Tenant credential storage is unavailable \u2014 the app database is not configured or failed to start."
|
|
30561
|
-
},
|
|
30562
|
-
503
|
|
30563
|
-
)
|
|
30564
|
-
};
|
|
30565
|
-
}
|
|
30566
|
-
return { mode: "tenant", storage, orgId: tenantOrgId(tenant), userId: tenant.userId };
|
|
30567
|
-
}
|
|
30568
|
-
async function listTenantCredentialsForRequest({
|
|
30569
|
-
c,
|
|
30570
|
-
auth,
|
|
30571
|
-
credentials
|
|
30572
|
-
}) {
|
|
30573
|
-
await auth.ensureUser(c);
|
|
30574
|
-
const tenant = auth.tenant(c);
|
|
30575
|
-
if (!tenant) return auth.enabled() ? [] : void 0;
|
|
30576
|
-
const storage = await getTenantCredentialsStorage(credentials);
|
|
30577
|
-
if (!storage) return [];
|
|
30578
|
-
return storage.listCredentials(tenantOrgId(tenant), tenant.userId);
|
|
30579
|
-
}
|
|
30580
|
-
|
|
30581
|
-
// src/routes/config.ts
|
|
30582
30830
|
function loose6(c) {
|
|
30583
30831
|
return c;
|
|
30584
30832
|
}
|
|
@@ -30622,15 +30870,39 @@ async function listProviders({
|
|
|
30622
30870
|
}
|
|
30623
30871
|
return Array.from(seen.values()).sort((a, b) => a.provider.localeCompare(b.provider));
|
|
30624
30872
|
}
|
|
30625
|
-
function
|
|
30626
|
-
|
|
30627
|
-
|
|
30628
|
-
|
|
30629
|
-
|
|
30630
|
-
|
|
30631
|
-
|
|
30632
|
-
|
|
30633
|
-
|
|
30873
|
+
function toCustomProviderInfo(record) {
|
|
30874
|
+
return {
|
|
30875
|
+
id: record.providerId,
|
|
30876
|
+
name: record.name,
|
|
30877
|
+
url: record.url,
|
|
30878
|
+
hasApiKey: Boolean(record.apiKey),
|
|
30879
|
+
models: record.models
|
|
30880
|
+
};
|
|
30881
|
+
}
|
|
30882
|
+
async function resolveCustomProvidersContext({
|
|
30883
|
+
c,
|
|
30884
|
+
auth,
|
|
30885
|
+
customProviders
|
|
30886
|
+
}) {
|
|
30887
|
+
await auth.ensureUser(c);
|
|
30888
|
+
const tenant = auth.tenant(c);
|
|
30889
|
+
if (!tenant && auth.enabled()) return { response: c.json({ error: "unauthorized" }, 401) };
|
|
30890
|
+
if (customProviders) {
|
|
30891
|
+
try {
|
|
30892
|
+
await customProviders.ensureReady();
|
|
30893
|
+
return tenant ? { storage: customProviders, orgId: tenantOrgId(tenant), userId: tenant.userId } : { storage: customProviders, orgId: "local", userId: "local" };
|
|
30894
|
+
} catch {
|
|
30895
|
+
}
|
|
30896
|
+
}
|
|
30897
|
+
return {
|
|
30898
|
+
response: c.json(
|
|
30899
|
+
{
|
|
30900
|
+
error: "custom_providers_unavailable",
|
|
30901
|
+
message: "Custom provider storage is unavailable \u2014 the app database is not configured or failed to start."
|
|
30902
|
+
},
|
|
30903
|
+
503
|
|
30904
|
+
)
|
|
30905
|
+
};
|
|
30634
30906
|
}
|
|
30635
30907
|
function parseCustomProviderBody(body) {
|
|
30636
30908
|
if (!body || typeof body !== "object") return { error: "Invalid JSON body" };
|
|
@@ -30695,15 +30967,6 @@ async function buildProviderAccess({
|
|
|
30695
30967
|
function canUseModelProvider(access, provider) {
|
|
30696
30968
|
return Boolean(access[provider]);
|
|
30697
30969
|
}
|
|
30698
|
-
async function getTenantModelPacksStorage(modelPacks) {
|
|
30699
|
-
if (!modelPacks) return void 0;
|
|
30700
|
-
try {
|
|
30701
|
-
await modelPacks.ensureReady();
|
|
30702
|
-
} catch {
|
|
30703
|
-
return void 0;
|
|
30704
|
-
}
|
|
30705
|
-
return modelPacks;
|
|
30706
|
-
}
|
|
30707
30970
|
async function resolvePackContext({
|
|
30708
30971
|
c,
|
|
30709
30972
|
auth,
|
|
@@ -30711,23 +30974,23 @@ async function resolvePackContext({
|
|
|
30711
30974
|
}) {
|
|
30712
30975
|
await auth.ensureUser(c);
|
|
30713
30976
|
const tenant = auth.tenant(c);
|
|
30714
|
-
if (!tenant) {
|
|
30715
|
-
|
|
30716
|
-
|
|
30717
|
-
|
|
30718
|
-
|
|
30719
|
-
|
|
30720
|
-
|
|
30721
|
-
response: c.json(
|
|
30722
|
-
{
|
|
30723
|
-
error: "model_packs_unavailable",
|
|
30724
|
-
message: "Model pack storage is unavailable \u2014 the app database is not configured or failed to start."
|
|
30725
|
-
},
|
|
30726
|
-
503
|
|
30727
|
-
)
|
|
30728
|
-
};
|
|
30977
|
+
if (!tenant && auth.enabled()) return { response: c.json({ error: "unauthorized" }, 401) };
|
|
30978
|
+
if (modelPacks) {
|
|
30979
|
+
try {
|
|
30980
|
+
await modelPacks.ensureReady();
|
|
30981
|
+
return tenant ? { storage: modelPacks, orgId: tenantOrgId(tenant), userId: tenant.userId } : { storage: modelPacks, orgId: "local", userId: "local" };
|
|
30982
|
+
} catch {
|
|
30983
|
+
}
|
|
30729
30984
|
}
|
|
30730
|
-
return {
|
|
30985
|
+
return {
|
|
30986
|
+
response: c.json(
|
|
30987
|
+
{
|
|
30988
|
+
error: "model_packs_unavailable",
|
|
30989
|
+
message: "Model pack storage is unavailable \u2014 the app database is not configured or failed to start."
|
|
30990
|
+
},
|
|
30991
|
+
503
|
|
30992
|
+
)
|
|
30993
|
+
};
|
|
30731
30994
|
}
|
|
30732
30995
|
function recordToModePack(record) {
|
|
30733
30996
|
return { id: `custom:${record.id}`, name: record.name, description: "Saved custom pack", models: record.models };
|
|
@@ -30740,7 +31003,7 @@ async function listModelPacks({
|
|
|
30740
31003
|
activePackId
|
|
30741
31004
|
}) {
|
|
30742
31005
|
const access = await buildProviderAccess({ controller, authStorage, tenantCredentials });
|
|
30743
|
-
const packs =
|
|
31006
|
+
const packs = [
|
|
30744
31007
|
...getAvailableModePacks(access),
|
|
30745
31008
|
...(await packContext.storage.list({ orgId: packContext.orgId })).map(recordToModePack)
|
|
30746
31009
|
];
|
|
@@ -30798,20 +31061,57 @@ function readOMConfig(session) {
|
|
|
30798
31061
|
observeAttachments: observeAttachments === true || observeAttachments === false ? observeAttachments : "auto"
|
|
30799
31062
|
};
|
|
30800
31063
|
}
|
|
30801
|
-
function
|
|
30802
|
-
|
|
30803
|
-
|
|
30804
|
-
|
|
30805
|
-
saveSettings(settings);
|
|
30806
|
-
}
|
|
30807
|
-
function persistOmRoleOverride({
|
|
30808
|
-
role,
|
|
30809
|
-
modelId,
|
|
30810
|
-
otherRoleCurrentModelId
|
|
31064
|
+
async function resolveMemorySettingsContext({
|
|
31065
|
+
c,
|
|
31066
|
+
auth,
|
|
31067
|
+
memorySettings
|
|
30811
31068
|
}) {
|
|
30812
|
-
|
|
30813
|
-
|
|
30814
|
-
|
|
31069
|
+
await auth.ensureUser(c);
|
|
31070
|
+
const tenant = auth.tenant(c);
|
|
31071
|
+
if (!tenant && auth.enabled()) return { response: c.json({ error: "unauthorized" }, 401) };
|
|
31072
|
+
if (memorySettings) {
|
|
31073
|
+
try {
|
|
31074
|
+
await memorySettings.ensureReady();
|
|
31075
|
+
return tenant ? { storage: memorySettings, orgId: tenantOrgId(tenant), userId: tenant.userId } : { storage: memorySettings, orgId: "local", userId: "local" };
|
|
31076
|
+
} catch {
|
|
31077
|
+
}
|
|
31078
|
+
}
|
|
31079
|
+
return {
|
|
31080
|
+
response: c.json(
|
|
31081
|
+
{
|
|
31082
|
+
error: "memory_settings_unavailable",
|
|
31083
|
+
message: "Memory settings storage is unavailable \u2014 the app database is not configured or failed to start."
|
|
31084
|
+
},
|
|
31085
|
+
503
|
|
31086
|
+
)
|
|
31087
|
+
};
|
|
31088
|
+
}
|
|
31089
|
+
async function persistMemorySettings(context, patch, fillIfUnset) {
|
|
31090
|
+
await context.storage.patch({ orgId: context.orgId, userId: context.userId, patch, fillIfUnset });
|
|
31091
|
+
}
|
|
31092
|
+
async function hydrateSessionMemorySettings(session, record) {
|
|
31093
|
+
for (const role of ["observer", "reflector"]) {
|
|
31094
|
+
const stored = role === "observer" ? record?.observerModelId : record?.reflectorModelId;
|
|
31095
|
+
const target = stored ?? DEFAULT_OM_MODEL_ID;
|
|
31096
|
+
if (session.om[role].modelId() !== target) {
|
|
31097
|
+
await session.om[role].switchModel({ modelId: target });
|
|
31098
|
+
}
|
|
31099
|
+
}
|
|
31100
|
+
const state = session.state.get() ?? {};
|
|
31101
|
+
const updates = {};
|
|
31102
|
+
const observationThreshold = record?.observationThreshold ?? DEFAULT_OBSERVATION_THRESHOLD;
|
|
31103
|
+
if (state.observationThreshold !== observationThreshold) {
|
|
31104
|
+
updates.observationThreshold = observationThreshold;
|
|
31105
|
+
}
|
|
31106
|
+
const reflectionThreshold = record?.reflectionThreshold ?? DEFAULT_REFLECTION_THRESHOLD;
|
|
31107
|
+
if (state.reflectionThreshold !== reflectionThreshold) {
|
|
31108
|
+
updates.reflectionThreshold = reflectionThreshold;
|
|
31109
|
+
}
|
|
31110
|
+
const observeAttachments = record?.observeAttachments ?? "auto";
|
|
31111
|
+
if ((state.observeAttachments ?? "auto") !== observeAttachments) {
|
|
31112
|
+
updates.observeAttachments = observeAttachments;
|
|
31113
|
+
}
|
|
31114
|
+
if (Object.keys(updates).length > 0) await session.state.set(updates);
|
|
30815
31115
|
}
|
|
30816
31116
|
var ConfigRoutes = class extends Route {
|
|
30817
31117
|
routes() {
|
|
@@ -30819,6 +31119,8 @@ var ConfigRoutes = class extends Route {
|
|
|
30819
31119
|
const { controller, authStorage, auth } = options;
|
|
30820
31120
|
const onCredentialsChanged = options.onCredentialsChanged ?? (() => {
|
|
30821
31121
|
});
|
|
31122
|
+
const onCustomProvidersChanged = options.onCustomProvidersChanged ?? (() => {
|
|
31123
|
+
});
|
|
30822
31124
|
return [
|
|
30823
31125
|
registerApiRoute7("/web/config/providers", {
|
|
30824
31126
|
method: "GET",
|
|
@@ -30910,14 +31212,22 @@ var ConfigRoutes = class extends Route {
|
|
|
30910
31212
|
}
|
|
30911
31213
|
}),
|
|
30912
31214
|
// ── Custom providers (OpenAI-compatible endpoints) ──────────────────────
|
|
30913
|
-
// Mirrors the TUI's /custom-providers command
|
|
30914
|
-
// (
|
|
31215
|
+
// Mirrors the TUI's /custom-providers command, but backed by the
|
|
31216
|
+
// `custom-providers` domain (org rows in tenant mode, a sentinel `local`
|
|
31217
|
+
// org in no-auth mode) — the server never reads settings.json for these.
|
|
30915
31218
|
registerApiRoute7("/web/config/custom-providers", {
|
|
30916
31219
|
method: "GET",
|
|
30917
31220
|
requiresAuth: false,
|
|
30918
|
-
handler: (c) => {
|
|
31221
|
+
handler: async (c) => {
|
|
31222
|
+
const ctx = await resolveCustomProvidersContext({
|
|
31223
|
+
c: loose6(c),
|
|
31224
|
+
auth,
|
|
31225
|
+
customProviders: options.customProviders
|
|
31226
|
+
});
|
|
31227
|
+
if ("response" in ctx) return ctx.response;
|
|
30919
31228
|
try {
|
|
30920
|
-
|
|
31229
|
+
const records = await ctx.storage.list({ orgId: ctx.orgId });
|
|
31230
|
+
return c.json({ providers: records.map(toCustomProviderInfo) });
|
|
30921
31231
|
} catch (error) {
|
|
30922
31232
|
return c.json({ error: error instanceof Error ? error.message : String(error) }, 500);
|
|
30923
31233
|
}
|
|
@@ -30927,6 +31237,12 @@ var ConfigRoutes = class extends Route {
|
|
|
30927
31237
|
method: "POST",
|
|
30928
31238
|
requiresAuth: false,
|
|
30929
31239
|
handler: async (c) => {
|
|
31240
|
+
const ctx = await resolveCustomProvidersContext({
|
|
31241
|
+
c: loose6(c),
|
|
31242
|
+
auth,
|
|
31243
|
+
customProviders: options.customProviders
|
|
31244
|
+
});
|
|
31245
|
+
if ("response" in ctx) return ctx.response;
|
|
30930
31246
|
let body;
|
|
30931
31247
|
try {
|
|
30932
31248
|
body = await c.req.json();
|
|
@@ -30937,11 +31253,20 @@ var ConfigRoutes = class extends Route {
|
|
|
30937
31253
|
if ("error" in parsed) return c.json({ error: parsed.error }, 400);
|
|
30938
31254
|
const previousId = body && typeof body === "object" && typeof body.previousId === "string" ? body.previousId : void 0;
|
|
30939
31255
|
try {
|
|
30940
|
-
const
|
|
30941
|
-
|
|
30942
|
-
|
|
30943
|
-
|
|
30944
|
-
|
|
31256
|
+
const record = await ctx.storage.upsert({
|
|
31257
|
+
orgId: ctx.orgId,
|
|
31258
|
+
userId: ctx.userId,
|
|
31259
|
+
input: {
|
|
31260
|
+
providerId: getCustomProviderId(parsed.name),
|
|
31261
|
+
name: parsed.name,
|
|
31262
|
+
url: parsed.url,
|
|
31263
|
+
apiKey: parsed.apiKey,
|
|
31264
|
+
models: parsed.models
|
|
31265
|
+
},
|
|
31266
|
+
previousProviderId: previousId
|
|
31267
|
+
});
|
|
31268
|
+
onCustomProvidersChanged({ orgId: ctx.orgId });
|
|
31269
|
+
return c.json({ ok: true, provider: toCustomProviderInfo(record) });
|
|
30945
31270
|
} catch (error) {
|
|
30946
31271
|
return c.json({ error: error instanceof Error ? error.message : String(error) }, 500);
|
|
30947
31272
|
}
|
|
@@ -30950,12 +31275,17 @@ var ConfigRoutes = class extends Route {
|
|
|
30950
31275
|
registerApiRoute7("/web/config/custom-providers/:id", {
|
|
30951
31276
|
method: "DELETE",
|
|
30952
31277
|
requiresAuth: false,
|
|
30953
|
-
handler: (c) => {
|
|
31278
|
+
handler: async (c) => {
|
|
31279
|
+
const ctx = await resolveCustomProvidersContext({
|
|
31280
|
+
c: loose6(c),
|
|
31281
|
+
auth,
|
|
31282
|
+
customProviders: options.customProviders
|
|
31283
|
+
});
|
|
31284
|
+
if ("response" in ctx) return ctx.response;
|
|
30954
31285
|
const id = c.req.param("id");
|
|
30955
31286
|
try {
|
|
30956
|
-
|
|
30957
|
-
|
|
30958
|
-
saveSettings(settings);
|
|
31287
|
+
await ctx.storage.delete({ orgId: ctx.orgId, providerId: id });
|
|
31288
|
+
onCustomProvidersChanged({ orgId: ctx.orgId });
|
|
30959
31289
|
return c.json({ ok: true });
|
|
30960
31290
|
} catch (error) {
|
|
30961
31291
|
return c.json({ error: error instanceof Error ? error.message : String(error) }, 500);
|
|
@@ -30984,8 +31314,30 @@ var ConfigRoutes = class extends Route {
|
|
|
30984
31314
|
tenantCredentials
|
|
30985
31315
|
})
|
|
30986
31316
|
]);
|
|
31317
|
+
const catalog = models.filter((m) => canUseModelProvider(access, m.provider) && typeof m.id === "string").map((m) => ({ id: m.id, provider: m.provider, modelName: m.modelName, hasApiKey: true }));
|
|
31318
|
+
if (options.customProviders) {
|
|
31319
|
+
try {
|
|
31320
|
+
const ctx = await resolveCustomProvidersContext({
|
|
31321
|
+
c: loose6(c),
|
|
31322
|
+
auth,
|
|
31323
|
+
customProviders: options.customProviders
|
|
31324
|
+
});
|
|
31325
|
+
if (!("response" in ctx)) {
|
|
31326
|
+
const known = new Set(catalog.map((m) => m.id));
|
|
31327
|
+
for (const record of await ctx.storage.list({ orgId: ctx.orgId })) {
|
|
31328
|
+
for (const model of record.models) {
|
|
31329
|
+
const id = `${record.providerId}/${model}`;
|
|
31330
|
+
if (known.has(id)) continue;
|
|
31331
|
+
known.add(id);
|
|
31332
|
+
catalog.push({ id, provider: record.providerId, modelName: model, hasApiKey: true });
|
|
31333
|
+
}
|
|
31334
|
+
}
|
|
31335
|
+
}
|
|
31336
|
+
} catch {
|
|
31337
|
+
}
|
|
31338
|
+
}
|
|
30987
31339
|
return c.json({
|
|
30988
|
-
models:
|
|
31340
|
+
models: catalog.sort(
|
|
30989
31341
|
(a, b) => a.provider === b.provider ? a.id.localeCompare(b.id) : a.provider.localeCompare(b.provider)
|
|
30990
31342
|
)
|
|
30991
31343
|
});
|
|
@@ -30995,10 +31347,10 @@ var ConfigRoutes = class extends Route {
|
|
|
30995
31347
|
}
|
|
30996
31348
|
}),
|
|
30997
31349
|
// ── Model packs ─────────────────────────────────────────────────────────
|
|
30998
|
-
// Mirrors the TUI's /models-pack command. Custom-pack CRUD
|
|
30999
|
-
//
|
|
31000
|
-
//
|
|
31001
|
-
// the controller registry by resourceId.
|
|
31350
|
+
// Mirrors the TUI's /models-pack command. Custom-pack CRUD lives in the
|
|
31351
|
+
// model-packs storage domain (org-scoped, sentinel `local` org in no-auth
|
|
31352
|
+
// mode — never settings.json); activation is session-scoped and resolves
|
|
31353
|
+
// the session from the controller registry by resourceId.
|
|
31002
31354
|
registerApiRoute7("/web/config/model-packs", {
|
|
31003
31355
|
method: "GET",
|
|
31004
31356
|
requiresAuth: false,
|
|
@@ -31052,21 +31404,12 @@ var ConfigRoutes = class extends Route {
|
|
|
31052
31404
|
return c.json({ error: "models.build, models.plan and models.fast are required" }, 400);
|
|
31053
31405
|
}
|
|
31054
31406
|
try {
|
|
31055
|
-
|
|
31056
|
-
|
|
31057
|
-
|
|
31058
|
-
|
|
31059
|
-
|
|
31060
|
-
|
|
31061
|
-
return c.json({ ok: true, pack: recordToModePack(record) });
|
|
31062
|
-
}
|
|
31063
|
-
const settings = loadSettings();
|
|
31064
|
-
const entry = { name, models: { build, plan, fast }, createdAt: (/* @__PURE__ */ new Date()).toISOString() };
|
|
31065
|
-
const idx = settings.customModelPacks.findIndex((p) => p.name === name);
|
|
31066
|
-
if (idx >= 0) settings.customModelPacks[idx] = entry;
|
|
31067
|
-
else settings.customModelPacks.push(entry);
|
|
31068
|
-
saveSettings(settings);
|
|
31069
|
-
return c.json({ ok: true, pack: { id: `custom:${name}`, name, models: { build, plan, fast } } });
|
|
31407
|
+
const record = await packContext.storage.upsert({
|
|
31408
|
+
orgId: packContext.orgId,
|
|
31409
|
+
userId: packContext.userId,
|
|
31410
|
+
input: { name, models: { build, plan, fast } }
|
|
31411
|
+
});
|
|
31412
|
+
return c.json({ ok: true, pack: recordToModePack(record) });
|
|
31070
31413
|
} catch (error) {
|
|
31071
31414
|
return c.json({ error: error instanceof Error ? error.message : String(error) }, 500);
|
|
31072
31415
|
}
|
|
@@ -31080,15 +31423,9 @@ var ConfigRoutes = class extends Route {
|
|
|
31080
31423
|
if ("response" in packContext) return packContext.response;
|
|
31081
31424
|
const id = decodeURIComponent(c.req.param("id"));
|
|
31082
31425
|
try {
|
|
31083
|
-
|
|
31084
|
-
|
|
31085
|
-
|
|
31086
|
-
return deleted ? c.json({ ok: true }) : c.json({ error: `Unknown pack "${id}"` }, 404);
|
|
31087
|
-
}
|
|
31088
|
-
const settings = loadSettings();
|
|
31089
|
-
removeCustomPackFromSettings(settings, id);
|
|
31090
|
-
saveSettings(settings);
|
|
31091
|
-
return c.json({ ok: true });
|
|
31426
|
+
const recordId = id.startsWith("custom:") ? id.slice("custom:".length) : id;
|
|
31427
|
+
const deleted = await packContext.storage.delete({ orgId: packContext.orgId, id: recordId });
|
|
31428
|
+
return deleted ? c.json({ ok: true }) : c.json({ error: `Unknown pack "${id}"` }, 404);
|
|
31092
31429
|
} catch (error) {
|
|
31093
31430
|
return c.json({ error: error instanceof Error ? error.message : String(error) }, 500);
|
|
31094
31431
|
}
|
|
@@ -31135,8 +31472,10 @@ var ConfigRoutes = class extends Route {
|
|
|
31135
31472
|
}),
|
|
31136
31473
|
// ── Observational memory ──────────────────────────────────────────────────
|
|
31137
31474
|
// Mirrors the TUI's /om command. All five knobs are session-scoped (resolved
|
|
31138
|
-
// from the session, persisted to its state + thread setting)
|
|
31139
|
-
//
|
|
31475
|
+
// from the session, persisted to its state + thread setting) and durably
|
|
31476
|
+
// stored in the per-(org, user) `memory-settings` app table — never
|
|
31477
|
+
// settings.json. GET hydrates the session from the stored row first so the
|
|
31478
|
+
// DB, not the SDK's boot-time seed, is the source of truth.
|
|
31140
31479
|
registerApiRoute7("/web/config/om", {
|
|
31141
31480
|
method: "GET",
|
|
31142
31481
|
requiresAuth: false,
|
|
@@ -31144,9 +31483,17 @@ var ConfigRoutes = class extends Route {
|
|
|
31144
31483
|
const resourceId = c.req.query("resourceId");
|
|
31145
31484
|
const scope = c.req.query("scope") || void 0;
|
|
31146
31485
|
if (!resourceId) return c.json({ error: "Missing required query param: resourceId" }, 400);
|
|
31486
|
+
const context = await resolveMemorySettingsContext({
|
|
31487
|
+
c: loose6(c),
|
|
31488
|
+
auth,
|
|
31489
|
+
memorySettings: options.memorySettings
|
|
31490
|
+
});
|
|
31491
|
+
if ("response" in context) return context.response;
|
|
31147
31492
|
try {
|
|
31148
31493
|
const session = await controller.getSessionByResource?.(resourceId, scope);
|
|
31149
31494
|
if (!session) return c.json({ error: `No session for resourceId "${resourceId}"` }, 404);
|
|
31495
|
+
const record = await context.storage.get({ orgId: context.orgId, userId: context.userId });
|
|
31496
|
+
await hydrateSessionMemorySettings(session, record);
|
|
31150
31497
|
return c.json({ config: readOMConfig(session) });
|
|
31151
31498
|
} catch (error) {
|
|
31152
31499
|
return c.json({ error: error instanceof Error ? error.message : String(error) }, 500);
|
|
@@ -31172,13 +31519,24 @@ var ConfigRoutes = class extends Route {
|
|
|
31172
31519
|
const modelId = typeof body.modelId === "string" ? body.modelId.trim() : "";
|
|
31173
31520
|
if (!resourceId) return c.json({ error: "Missing required field: resourceId" }, 400);
|
|
31174
31521
|
if (!modelId) return c.json({ error: "Missing required field: modelId" }, 400);
|
|
31522
|
+
const context = await resolveMemorySettingsContext({
|
|
31523
|
+
c: loose6(c),
|
|
31524
|
+
auth,
|
|
31525
|
+
memorySettings: options.memorySettings
|
|
31526
|
+
});
|
|
31527
|
+
if ("response" in context) return context.response;
|
|
31175
31528
|
try {
|
|
31176
31529
|
const session = await controller.getSessionByResource?.(resourceId, scope);
|
|
31177
31530
|
if (!session) return c.json({ error: `No session for resourceId "${resourceId}"` }, 404);
|
|
31178
31531
|
const otherRole = role === "observer" ? session.om.reflector : session.om.observer;
|
|
31179
31532
|
const otherRoleCurrentModelId = otherRole.modelId() ?? null;
|
|
31180
31533
|
await session.om[role].switchModel({ modelId });
|
|
31181
|
-
|
|
31534
|
+
const otherKey = role === "observer" ? "reflectorModelId" : "observerModelId";
|
|
31535
|
+
await persistMemorySettings(
|
|
31536
|
+
context,
|
|
31537
|
+
{ [role === "observer" ? "observerModelId" : "reflectorModelId"]: modelId },
|
|
31538
|
+
otherRoleCurrentModelId ? { [otherKey]: otherRoleCurrentModelId } : void 0
|
|
31539
|
+
);
|
|
31182
31540
|
return c.json({ ok: true, config: readOMConfig(session) });
|
|
31183
31541
|
} catch (error) {
|
|
31184
31542
|
return c.json({ error: error instanceof Error ? error.message : String(error) }, 500);
|
|
@@ -31203,19 +31561,27 @@ var ConfigRoutes = class extends Route {
|
|
|
31203
31561
|
if (observation === void 0 && reflection === void 0) {
|
|
31204
31562
|
return c.json({ error: "Provide observationThreshold and/or reflectionThreshold (positive numbers)" }, 400);
|
|
31205
31563
|
}
|
|
31564
|
+
const context = await resolveMemorySettingsContext({
|
|
31565
|
+
c: loose6(c),
|
|
31566
|
+
auth,
|
|
31567
|
+
memorySettings: options.memorySettings
|
|
31568
|
+
});
|
|
31569
|
+
if ("response" in context) return context.response;
|
|
31206
31570
|
try {
|
|
31207
31571
|
const session = await controller.getSessionByResource?.(resourceId, scope);
|
|
31208
31572
|
if (!session) return c.json({ error: `No session for resourceId "${resourceId}"` }, 404);
|
|
31209
31573
|
if (observation !== void 0) {
|
|
31210
31574
|
await session.state.set({ observationThreshold: observation });
|
|
31211
31575
|
await session.thread.setSetting({ key: "observationThreshold", value: observation });
|
|
31212
|
-
persistOmThreshold({ role: "observation", value: observation });
|
|
31213
31576
|
}
|
|
31214
31577
|
if (reflection !== void 0) {
|
|
31215
31578
|
await session.state.set({ reflectionThreshold: reflection });
|
|
31216
31579
|
await session.thread.setSetting({ key: "reflectionThreshold", value: reflection });
|
|
31217
|
-
persistOmThreshold({ role: "reflection", value: reflection });
|
|
31218
31580
|
}
|
|
31581
|
+
await persistMemorySettings(context, {
|
|
31582
|
+
...observation !== void 0 ? { observationThreshold: observation } : {},
|
|
31583
|
+
...reflection !== void 0 ? { reflectionThreshold: reflection } : {}
|
|
31584
|
+
});
|
|
31219
31585
|
return c.json({ ok: true, config: readOMConfig(session) });
|
|
31220
31586
|
} catch (error) {
|
|
31221
31587
|
return c.json({ error: error instanceof Error ? error.message : String(error) }, 500);
|
|
@@ -31240,12 +31606,18 @@ var ConfigRoutes = class extends Route {
|
|
|
31240
31606
|
if (raw !== "auto" && raw !== true && raw !== false) {
|
|
31241
31607
|
return c.json({ error: "value must be 'auto', true, or false" }, 400);
|
|
31242
31608
|
}
|
|
31609
|
+
const context = await resolveMemorySettingsContext({
|
|
31610
|
+
c: loose6(c),
|
|
31611
|
+
auth,
|
|
31612
|
+
memorySettings: options.memorySettings
|
|
31613
|
+
});
|
|
31614
|
+
if ("response" in context) return context.response;
|
|
31243
31615
|
try {
|
|
31244
31616
|
const session = await controller.getSessionByResource?.(resourceId, scope);
|
|
31245
31617
|
if (!session) return c.json({ error: `No session for resourceId "${resourceId}"` }, 404);
|
|
31246
31618
|
await session.state.set({ observeAttachments: value });
|
|
31247
31619
|
await session.thread.setSetting({ key: "observeAttachments", value });
|
|
31248
|
-
|
|
31620
|
+
await persistMemorySettings(context, { observeAttachments: value });
|
|
31249
31621
|
return c.json({ ok: true, config: readOMConfig(session) });
|
|
31250
31622
|
} catch (error) {
|
|
31251
31623
|
return c.json({ error: error instanceof Error ? error.message : String(error) }, 500);
|
|
@@ -32463,7 +32835,7 @@ var SkillRoutes = class extends Route {
|
|
|
32463
32835
|
// src/routes/tenant-credentials.ts
|
|
32464
32836
|
import { setCredentialStoreProvider } from "@mastra/code-sdk/agents/credential-resolver";
|
|
32465
32837
|
import { getOAuthProvider } from "@mastra/code-sdk/auth/storage";
|
|
32466
|
-
var
|
|
32838
|
+
var SNAPSHOT_TTL_MS2 = 15e3;
|
|
32467
32839
|
var MAX_CACHED_TENANTS = 1e3;
|
|
32468
32840
|
var TenantCredentialStore = class {
|
|
32469
32841
|
allowEnvironmentFallback = false;
|
|
@@ -32480,7 +32852,7 @@ var TenantCredentialStore = class {
|
|
|
32480
32852
|
}
|
|
32481
32853
|
/** Hydrate the snapshot when stale; coalesces concurrent callers. */
|
|
32482
32854
|
async ensureFresh(now = Date.now()) {
|
|
32483
|
-
if (now - this.#fetchedAt <
|
|
32855
|
+
if (now - this.#fetchedAt < SNAPSHOT_TTL_MS2) return;
|
|
32484
32856
|
this.#hydrating ??= this.#hydrate().finally(() => {
|
|
32485
32857
|
this.#hydrating = void 0;
|
|
32486
32858
|
});
|
|
@@ -32502,7 +32874,7 @@ var TenantCredentialStore = class {
|
|
|
32502
32874
|
}
|
|
32503
32875
|
/** Sync by contract; kicks a background re-hydrate when the snapshot is stale. */
|
|
32504
32876
|
reload() {
|
|
32505
|
-
if (Date.now() - this.#fetchedAt >=
|
|
32877
|
+
if (Date.now() - this.#fetchedAt >= SNAPSHOT_TTL_MS2) {
|
|
32506
32878
|
void this.ensureFresh().catch(() => {
|
|
32507
32879
|
});
|
|
32508
32880
|
}
|
|
@@ -34886,7 +35258,10 @@ function assembleFactoryApiRoutes(deps) {
|
|
|
34886
35258
|
authStorage: deps.authStorage,
|
|
34887
35259
|
modelCredentials: deps.domains.modelCredentials,
|
|
34888
35260
|
modelPacks: deps.domains.modelPacks,
|
|
34889
|
-
|
|
35261
|
+
memorySettings: deps.domains.memorySettings,
|
|
35262
|
+
customProviders: deps.domains.customProviders,
|
|
35263
|
+
onCredentialsChanged: invalidateTenantCredentialSnapshots,
|
|
35264
|
+
onCustomProvidersChanged: invalidateCustomProvidersSnapshots
|
|
34890
35265
|
}).routes(),
|
|
34891
35266
|
...new OAuthRoutes({
|
|
34892
35267
|
auth: deps.auth,
|
|
@@ -36051,10 +36426,13 @@ var MIME = {
|
|
|
36051
36426
|
};
|
|
36052
36427
|
var SERVER_PREFIXES = ["/api", "/web", "/auth"];
|
|
36053
36428
|
function resolveUiDistDir() {
|
|
36429
|
+
const projectRoot = process.env.MASTRA_PROJECT_ROOT?.trim();
|
|
36054
36430
|
const candidates = [
|
|
36055
36431
|
process.env.MASTRACODE_UI_DIST,
|
|
36056
|
-
|
|
36057
|
-
|
|
36432
|
+
resolve2(process.cwd(), "factory"),
|
|
36433
|
+
join2(dirname(fileURLToPath(import.meta.url)), "factory"),
|
|
36434
|
+
projectRoot && resolve2(projectRoot, "src/mastra/public/factory"),
|
|
36435
|
+
resolve2(process.cwd(), "src/mastra/public/factory")
|
|
36058
36436
|
];
|
|
36059
36437
|
for (const candidate of candidates) {
|
|
36060
36438
|
if (candidate && existsSync(join2(candidate, "index.html"))) return resolve2(candidate);
|
|
@@ -36480,8 +36858,154 @@ var AuditDomain = class {
|
|
|
36480
36858
|
}
|
|
36481
36859
|
};
|
|
36482
36860
|
|
|
36483
|
-
// src/storage/domains/
|
|
36861
|
+
// src/storage/domains/custom-providers/base.ts
|
|
36484
36862
|
import { FactoryStorageDomain as FactoryStorageDomain5, UniqueViolationError as UniqueViolationError5 } from "@mastra/core/storage";
|
|
36863
|
+
var CUSTOM_PROVIDERS_SCHEMA = {
|
|
36864
|
+
name: "custom_providers",
|
|
36865
|
+
columns: {
|
|
36866
|
+
id: { type: "uuid-pk" },
|
|
36867
|
+
org_id: { type: "text" },
|
|
36868
|
+
created_by: { type: "text" },
|
|
36869
|
+
provider_id: { type: "text" },
|
|
36870
|
+
name: { type: "text" },
|
|
36871
|
+
url: { type: "text" },
|
|
36872
|
+
api_key: { type: "text", nullable: true },
|
|
36873
|
+
models: { type: "json" },
|
|
36874
|
+
created_at: { type: "timestamp" },
|
|
36875
|
+
updated_at: { type: "timestamp" }
|
|
36876
|
+
},
|
|
36877
|
+
uniqueIndexes: [{ name: "custom_providers_org_provider_key", columns: ["org_id", "provider_id"] }]
|
|
36878
|
+
};
|
|
36879
|
+
function toRecord(row) {
|
|
36880
|
+
return {
|
|
36881
|
+
id: row.id,
|
|
36882
|
+
orgId: row.org_id,
|
|
36883
|
+
createdBy: row.created_by,
|
|
36884
|
+
providerId: row.provider_id,
|
|
36885
|
+
name: row.name,
|
|
36886
|
+
url: row.url,
|
|
36887
|
+
apiKey: row.api_key,
|
|
36888
|
+
models: Array.isArray(row.models) ? row.models : [],
|
|
36889
|
+
createdAt: row.created_at,
|
|
36890
|
+
updatedAt: row.updated_at
|
|
36891
|
+
};
|
|
36892
|
+
}
|
|
36893
|
+
var CustomProvidersStorage = class extends FactoryStorageDomain5 {
|
|
36894
|
+
constructor() {
|
|
36895
|
+
super("custom-providers");
|
|
36896
|
+
}
|
|
36897
|
+
async init() {
|
|
36898
|
+
await this.ensureCollections([CUSTOM_PROVIDERS_SCHEMA]);
|
|
36899
|
+
}
|
|
36900
|
+
async dangerouslyClearAll() {
|
|
36901
|
+
await this.ops.deleteMany("custom_providers", {});
|
|
36902
|
+
}
|
|
36903
|
+
get #db() {
|
|
36904
|
+
return this.ops;
|
|
36905
|
+
}
|
|
36906
|
+
/**
|
|
36907
|
+
* Create or wholesale-replace a provider by `(orgId, providerId)` — mirrors
|
|
36908
|
+
* the settings.json upsert semantics (no key retention: an absent `apiKey`
|
|
36909
|
+
* clears the stored key). `previousProviderId` renames the provider: the
|
|
36910
|
+
* common path is a single atomic in-place update of the old row; renaming
|
|
36911
|
+
* onto an id that already exists overwrites the target first and removes the
|
|
36912
|
+
* old row last, so no interleaving or failure can lose the provider.
|
|
36913
|
+
*/
|
|
36914
|
+
async upsert({
|
|
36915
|
+
orgId,
|
|
36916
|
+
userId,
|
|
36917
|
+
input,
|
|
36918
|
+
previousProviderId
|
|
36919
|
+
}) {
|
|
36920
|
+
const now = /* @__PURE__ */ new Date();
|
|
36921
|
+
const renameFrom = previousProviderId && previousProviderId !== input.providerId ? previousProviderId : void 0;
|
|
36922
|
+
if (renameFrom) {
|
|
36923
|
+
const target = await this.#db.findOne("custom_providers", {
|
|
36924
|
+
org_id: orgId,
|
|
36925
|
+
provider_id: input.providerId
|
|
36926
|
+
});
|
|
36927
|
+
if (!target) {
|
|
36928
|
+
const renamed = await this.#db.updateAtomic(
|
|
36929
|
+
"custom_providers",
|
|
36930
|
+
{ org_id: orgId, provider_id: renameFrom },
|
|
36931
|
+
() => ({
|
|
36932
|
+
provider_id: input.providerId,
|
|
36933
|
+
name: input.name,
|
|
36934
|
+
url: input.url,
|
|
36935
|
+
api_key: input.apiKey ?? null,
|
|
36936
|
+
models: input.models,
|
|
36937
|
+
updated_at: now
|
|
36938
|
+
})
|
|
36939
|
+
);
|
|
36940
|
+
if (renamed) return toRecord(renamed);
|
|
36941
|
+
}
|
|
36942
|
+
}
|
|
36943
|
+
const record = await this.#write({ orgId, userId, input, now });
|
|
36944
|
+
if (renameFrom) {
|
|
36945
|
+
await this.#db.deleteMany("custom_providers", { org_id: orgId, provider_id: renameFrom });
|
|
36946
|
+
}
|
|
36947
|
+
return record;
|
|
36948
|
+
}
|
|
36949
|
+
/**
|
|
36950
|
+
* Update-first, then insert-and-catch-unique-violation (see `queue_health`):
|
|
36951
|
+
* concurrent creates of the same provider both succeed (last write wins on
|
|
36952
|
+
* the single row) instead of one failing on the unique index, and a row
|
|
36953
|
+
* deleted mid-flight is recreated rather than reported as a stale success.
|
|
36954
|
+
*/
|
|
36955
|
+
async #write({
|
|
36956
|
+
orgId,
|
|
36957
|
+
userId,
|
|
36958
|
+
input,
|
|
36959
|
+
now
|
|
36960
|
+
}) {
|
|
36961
|
+
const updateExisting = () => this.#db.updateAtomic(
|
|
36962
|
+
"custom_providers",
|
|
36963
|
+
{ org_id: orgId, provider_id: input.providerId },
|
|
36964
|
+
() => ({
|
|
36965
|
+
name: input.name,
|
|
36966
|
+
url: input.url,
|
|
36967
|
+
api_key: input.apiKey ?? null,
|
|
36968
|
+
models: input.models,
|
|
36969
|
+
updated_at: now
|
|
36970
|
+
})
|
|
36971
|
+
);
|
|
36972
|
+
const updated = await updateExisting();
|
|
36973
|
+
if (updated) return toRecord(updated);
|
|
36974
|
+
try {
|
|
36975
|
+
const row = await this.#db.insertOne("custom_providers", {
|
|
36976
|
+
org_id: orgId,
|
|
36977
|
+
created_by: userId,
|
|
36978
|
+
provider_id: input.providerId,
|
|
36979
|
+
name: input.name,
|
|
36980
|
+
url: input.url,
|
|
36981
|
+
api_key: input.apiKey ?? null,
|
|
36982
|
+
models: input.models,
|
|
36983
|
+
created_at: now,
|
|
36984
|
+
updated_at: now
|
|
36985
|
+
});
|
|
36986
|
+
return toRecord(row);
|
|
36987
|
+
} catch (error) {
|
|
36988
|
+
if (!(error instanceof UniqueViolationError5)) throw error;
|
|
36989
|
+
const row = await updateExisting();
|
|
36990
|
+
if (!row) throw error;
|
|
36991
|
+
return toRecord(row);
|
|
36992
|
+
}
|
|
36993
|
+
}
|
|
36994
|
+
async list({ orgId }) {
|
|
36995
|
+
const rows = await this.#db.findMany(
|
|
36996
|
+
"custom_providers",
|
|
36997
|
+
{ org_id: orgId },
|
|
36998
|
+
{ orderBy: [["name", "asc"]] }
|
|
36999
|
+
);
|
|
37000
|
+
return rows.map(toRecord);
|
|
37001
|
+
}
|
|
37002
|
+
async delete({ orgId, providerId }) {
|
|
37003
|
+
return await this.#db.deleteMany("custom_providers", { org_id: orgId, provider_id: providerId }) > 0;
|
|
37004
|
+
}
|
|
37005
|
+
};
|
|
37006
|
+
|
|
37007
|
+
// src/storage/domains/intake/base.ts
|
|
37008
|
+
import { FactoryStorageDomain as FactoryStorageDomain6, UniqueViolationError as UniqueViolationError6 } from "@mastra/core/storage";
|
|
36485
37009
|
var DEFAULT_INTAKE_CONFIG = {};
|
|
36486
37010
|
var INTAKE_SETTINGS_SCHEMA = {
|
|
36487
37011
|
name: "intake_settings",
|
|
@@ -36495,7 +37019,7 @@ var INTAKE_SETTINGS_SCHEMA = {
|
|
|
36495
37019
|
},
|
|
36496
37020
|
uniqueIndexes: [{ name: "intake_settings_org_user_unique", columns: ["org_id", "user_id"] }]
|
|
36497
37021
|
};
|
|
36498
|
-
var IntakeStorage = class extends
|
|
37022
|
+
var IntakeStorage = class extends FactoryStorageDomain6 {
|
|
36499
37023
|
constructor() {
|
|
36500
37024
|
super("intake");
|
|
36501
37025
|
}
|
|
@@ -36531,14 +37055,14 @@ var IntakeStorage = class extends FactoryStorageDomain5 {
|
|
|
36531
37055
|
try {
|
|
36532
37056
|
await this.#db.insertOne("intake_settings", { ...where, config, created_at: now, updated_at: now });
|
|
36533
37057
|
} catch (error) {
|
|
36534
|
-
if (!(error instanceof
|
|
37058
|
+
if (!(error instanceof UniqueViolationError6)) throw error;
|
|
36535
37059
|
await this.#db.updateMany("intake_settings", where, { config, updated_at: now });
|
|
36536
37060
|
}
|
|
36537
37061
|
}
|
|
36538
37062
|
};
|
|
36539
37063
|
|
|
36540
37064
|
// src/storage/domains/integrations/base.ts
|
|
36541
|
-
import { FactoryStorageDomain as
|
|
37065
|
+
import { FactoryStorageDomain as FactoryStorageDomain7, UniqueViolationError as UniqueViolationError7 } from "@mastra/core/storage";
|
|
36542
37066
|
var INTEGRATION_CONNECTIONS_SCHEMA = {
|
|
36543
37067
|
name: "integration_connections",
|
|
36544
37068
|
columns: {
|
|
@@ -36591,7 +37115,7 @@ var INTEGRATION_SETTINGS_SCHEMA = {
|
|
|
36591
37115
|
{ name: "integration_settings_integration_org_user_unique", columns: ["integration_id", "org_id", "user_id"] }
|
|
36592
37116
|
]
|
|
36593
37117
|
};
|
|
36594
|
-
var IntegrationStorage = class extends
|
|
37118
|
+
var IntegrationStorage = class extends FactoryStorageDomain7 {
|
|
36595
37119
|
constructor() {
|
|
36596
37120
|
super("integrations");
|
|
36597
37121
|
}
|
|
@@ -36667,7 +37191,7 @@ var IntegrationStorage = class extends FactoryStorageDomain6 {
|
|
|
36667
37191
|
});
|
|
36668
37192
|
return;
|
|
36669
37193
|
} catch (error) {
|
|
36670
|
-
if (!(error instanceof
|
|
37194
|
+
if (!(error instanceof UniqueViolationError7)) throw error;
|
|
36671
37195
|
lastError = error;
|
|
36672
37196
|
}
|
|
36673
37197
|
}
|
|
@@ -36772,7 +37296,7 @@ var IntegrationStorage = class extends FactoryStorageDomain6 {
|
|
|
36772
37296
|
});
|
|
36773
37297
|
return;
|
|
36774
37298
|
} catch (error) {
|
|
36775
|
-
if (!(error instanceof
|
|
37299
|
+
if (!(error instanceof UniqueViolationError7)) throw error;
|
|
36776
37300
|
lastError = error;
|
|
36777
37301
|
}
|
|
36778
37302
|
}
|
|
@@ -36783,8 +37307,115 @@ var IntegrationStorage = class extends FactoryStorageDomain6 {
|
|
|
36783
37307
|
}
|
|
36784
37308
|
};
|
|
36785
37309
|
|
|
37310
|
+
// src/storage/domains/memory-settings/base.ts
|
|
37311
|
+
import { FactoryStorageDomain as FactoryStorageDomain8, UniqueViolationError as UniqueViolationError8 } from "@mastra/core/storage";
|
|
37312
|
+
var MEMORY_SETTINGS_SCHEMA = {
|
|
37313
|
+
name: "memory_settings",
|
|
37314
|
+
columns: {
|
|
37315
|
+
id: { type: "uuid-pk" },
|
|
37316
|
+
org_id: { type: "text" },
|
|
37317
|
+
user_id: { type: "text" },
|
|
37318
|
+
observer_model_id: { type: "text", nullable: true },
|
|
37319
|
+
reflector_model_id: { type: "text", nullable: true },
|
|
37320
|
+
observation_threshold: { type: "integer", nullable: true },
|
|
37321
|
+
reflection_threshold: { type: "integer", nullable: true },
|
|
37322
|
+
// 'auto' | true | false — json keeps the tri-state without string encoding.
|
|
37323
|
+
observe_attachments: { type: "json", nullable: true },
|
|
37324
|
+
created_at: { type: "timestamp" },
|
|
37325
|
+
updated_at: { type: "timestamp" }
|
|
37326
|
+
},
|
|
37327
|
+
uniqueIndexes: [{ name: "memory_settings_org_user_key", columns: ["org_id", "user_id"] }]
|
|
37328
|
+
};
|
|
37329
|
+
function toRecord2(row) {
|
|
37330
|
+
return {
|
|
37331
|
+
orgId: row.org_id,
|
|
37332
|
+
userId: row.user_id,
|
|
37333
|
+
observerModelId: row.observer_model_id,
|
|
37334
|
+
reflectorModelId: row.reflector_model_id,
|
|
37335
|
+
observationThreshold: row.observation_threshold,
|
|
37336
|
+
reflectionThreshold: row.reflection_threshold,
|
|
37337
|
+
observeAttachments: row.observe_attachments,
|
|
37338
|
+
createdAt: row.created_at,
|
|
37339
|
+
updatedAt: row.updated_at
|
|
37340
|
+
};
|
|
37341
|
+
}
|
|
37342
|
+
function patchToColumns(patch) {
|
|
37343
|
+
const columns = {};
|
|
37344
|
+
if (patch.observerModelId !== void 0) columns.observer_model_id = patch.observerModelId;
|
|
37345
|
+
if (patch.reflectorModelId !== void 0) columns.reflector_model_id = patch.reflectorModelId;
|
|
37346
|
+
if (patch.observationThreshold !== void 0) columns.observation_threshold = patch.observationThreshold;
|
|
37347
|
+
if (patch.reflectionThreshold !== void 0) columns.reflection_threshold = patch.reflectionThreshold;
|
|
37348
|
+
if (patch.observeAttachments !== void 0) columns.observe_attachments = patch.observeAttachments;
|
|
37349
|
+
return columns;
|
|
37350
|
+
}
|
|
37351
|
+
var MemorySettingsStorage = class extends FactoryStorageDomain8 {
|
|
37352
|
+
constructor() {
|
|
37353
|
+
super("memory-settings");
|
|
37354
|
+
}
|
|
37355
|
+
async init() {
|
|
37356
|
+
await this.ensureCollections([MEMORY_SETTINGS_SCHEMA]);
|
|
37357
|
+
}
|
|
37358
|
+
async dangerouslyClearAll() {
|
|
37359
|
+
await this.ops.deleteMany("memory_settings", {});
|
|
37360
|
+
}
|
|
37361
|
+
get #db() {
|
|
37362
|
+
return this.ops;
|
|
37363
|
+
}
|
|
37364
|
+
async get({ orgId, userId }) {
|
|
37365
|
+
const row = await this.#db.findOne("memory_settings", { org_id: orgId, user_id: userId });
|
|
37366
|
+
return row ? toRecord2(row) : null;
|
|
37367
|
+
}
|
|
37368
|
+
/**
|
|
37369
|
+
* Upsert the user's row, writing only the knobs present in `patch`.
|
|
37370
|
+
* `fillIfUnset` knobs are written only where the stored value is still
|
|
37371
|
+
* `NULL`, decided inside the atomic update so concurrent explicit writes
|
|
37372
|
+
* win over fills. Concurrent first writes are resolved via the shared
|
|
37373
|
+
* insert-then-catch-unique-violation pattern (see `queue_health`).
|
|
37374
|
+
*/
|
|
37375
|
+
async patch({
|
|
37376
|
+
orgId,
|
|
37377
|
+
userId,
|
|
37378
|
+
patch,
|
|
37379
|
+
fillIfUnset
|
|
37380
|
+
}) {
|
|
37381
|
+
const now = /* @__PURE__ */ new Date();
|
|
37382
|
+
const updateExisting = () => this.#db.updateAtomic("memory_settings", { org_id: orgId, user_id: userId }, (row) => {
|
|
37383
|
+
const columns = { ...patchToColumns(patch), updated_at: now };
|
|
37384
|
+
if (fillIfUnset?.observerModelId !== void 0 && row.observer_model_id == null) {
|
|
37385
|
+
columns.observer_model_id = patch.observerModelId ?? fillIfUnset.observerModelId;
|
|
37386
|
+
}
|
|
37387
|
+
if (fillIfUnset?.reflectorModelId !== void 0 && row.reflector_model_id == null) {
|
|
37388
|
+
columns.reflector_model_id = patch.reflectorModelId ?? fillIfUnset.reflectorModelId;
|
|
37389
|
+
}
|
|
37390
|
+
return columns;
|
|
37391
|
+
});
|
|
37392
|
+
const updated = await updateExisting();
|
|
37393
|
+
if (updated) return toRecord2(updated);
|
|
37394
|
+
try {
|
|
37395
|
+
const row = await this.#db.insertOne("memory_settings", {
|
|
37396
|
+
org_id: orgId,
|
|
37397
|
+
user_id: userId,
|
|
37398
|
+
observer_model_id: fillIfUnset?.observerModelId ?? null,
|
|
37399
|
+
reflector_model_id: fillIfUnset?.reflectorModelId ?? null,
|
|
37400
|
+
observation_threshold: null,
|
|
37401
|
+
reflection_threshold: null,
|
|
37402
|
+
observe_attachments: null,
|
|
37403
|
+
...patchToColumns(patch),
|
|
37404
|
+
created_at: now,
|
|
37405
|
+
updated_at: now
|
|
37406
|
+
});
|
|
37407
|
+
return toRecord2(row);
|
|
37408
|
+
} catch (error) {
|
|
37409
|
+
if (!(error instanceof UniqueViolationError8)) throw error;
|
|
37410
|
+
const row = await updateExisting();
|
|
37411
|
+
if (!row) throw error;
|
|
37412
|
+
return toRecord2(row);
|
|
37413
|
+
}
|
|
37414
|
+
}
|
|
37415
|
+
};
|
|
37416
|
+
|
|
36786
37417
|
// src/storage/domains/model-packs/base.ts
|
|
36787
|
-
import { FactoryStorageDomain as
|
|
37418
|
+
import { FactoryStorageDomain as FactoryStorageDomain9 } from "@mastra/core/storage";
|
|
36788
37419
|
var MODEL_PACKS_SCHEMA = {
|
|
36789
37420
|
name: "model_packs",
|
|
36790
37421
|
columns: {
|
|
@@ -36811,7 +37442,7 @@ function toModelPack(row) {
|
|
|
36811
37442
|
updatedAt: row.updated_at
|
|
36812
37443
|
};
|
|
36813
37444
|
}
|
|
36814
|
-
var ModelPacksStorage = class extends
|
|
37445
|
+
var ModelPacksStorage = class extends FactoryStorageDomain9 {
|
|
36815
37446
|
constructor() {
|
|
36816
37447
|
super("model-packs");
|
|
36817
37448
|
}
|
|
@@ -36875,7 +37506,7 @@ var ModelPacksStorage = class extends FactoryStorageDomain7 {
|
|
|
36875
37506
|
};
|
|
36876
37507
|
|
|
36877
37508
|
// src/storage/domains/projects/base.ts
|
|
36878
|
-
import { FactoryStorageDomain as
|
|
37509
|
+
import { FactoryStorageDomain as FactoryStorageDomain10 } from "@mastra/core/storage";
|
|
36879
37510
|
var FACTORY_PROJECTS_SCHEMA = {
|
|
36880
37511
|
name: "factory_projects",
|
|
36881
37512
|
columns: {
|
|
@@ -36902,7 +37533,7 @@ function toFactoryProject(row) {
|
|
|
36902
37533
|
updatedAt: row.updated_at
|
|
36903
37534
|
};
|
|
36904
37535
|
}
|
|
36905
|
-
var FactoryProjectsStorage = class extends
|
|
37536
|
+
var FactoryProjectsStorage = class extends FactoryStorageDomain10 {
|
|
36906
37537
|
constructor() {
|
|
36907
37538
|
super("projects");
|
|
36908
37539
|
}
|
|
@@ -36970,7 +37601,7 @@ var FactoryProjectsStorage = class extends FactoryStorageDomain8 {
|
|
|
36970
37601
|
};
|
|
36971
37602
|
|
|
36972
37603
|
// src/storage/domains/source-control/base.ts
|
|
36973
|
-
import { FactoryStorageDomain as
|
|
37604
|
+
import { FactoryStorageDomain as FactoryStorageDomain11, UniqueViolationError as UniqueViolationError9 } from "@mastra/core/storage";
|
|
36974
37605
|
var FACTORY_PROJECTS = "factory_projects";
|
|
36975
37606
|
var INSTALLATIONS = "source_control_installations";
|
|
36976
37607
|
var REPOSITORIES = "source_control_repositories";
|
|
@@ -37223,7 +37854,7 @@ function toSession(row) {
|
|
|
37223
37854
|
updatedAt: row.updated_at
|
|
37224
37855
|
};
|
|
37225
37856
|
}
|
|
37226
|
-
var SourceControlStorage = class extends
|
|
37857
|
+
var SourceControlStorage = class extends FactoryStorageDomain11 {
|
|
37227
37858
|
constructor() {
|
|
37228
37859
|
super("source-control");
|
|
37229
37860
|
}
|
|
@@ -37417,7 +38048,7 @@ var SourceControlStorage = class extends FactoryStorageDomain9 {
|
|
|
37417
38048
|
});
|
|
37418
38049
|
return toConnection(row);
|
|
37419
38050
|
} catch (error) {
|
|
37420
|
-
if (!(error instanceof
|
|
38051
|
+
if (!(error instanceof UniqueViolationError9)) throw error;
|
|
37421
38052
|
const row = await db().findOne(CONNECTIONS, {
|
|
37422
38053
|
factory_project_id: input.factoryProjectId,
|
|
37423
38054
|
integration_id: integrationId,
|
|
@@ -37546,7 +38177,7 @@ var SourceControlStorage = class extends FactoryStorageDomain9 {
|
|
|
37546
38177
|
});
|
|
37547
38178
|
return toSandbox(row);
|
|
37548
38179
|
} catch (error) {
|
|
37549
|
-
if (!(error instanceof
|
|
38180
|
+
if (!(error instanceof UniqueViolationError9)) throw error;
|
|
37550
38181
|
const row = await db().findOne(SANDBOXES, where);
|
|
37551
38182
|
if (!row) throw error;
|
|
37552
38183
|
return toSandbox(row);
|
|
@@ -37659,7 +38290,7 @@ var SourceControlStorage = class extends FactoryStorageDomain9 {
|
|
|
37659
38290
|
});
|
|
37660
38291
|
return toSession(row);
|
|
37661
38292
|
} catch (error) {
|
|
37662
|
-
if (!(error instanceof
|
|
38293
|
+
if (!(error instanceof UniqueViolationError9)) throw error;
|
|
37663
38294
|
const row = await db().findOne(SESSIONS, {
|
|
37664
38295
|
project_repository_id: input.projectRepositoryId,
|
|
37665
38296
|
user_id: input.userId,
|
|
@@ -37762,6 +38393,7 @@ var factorySkillExtension = {
|
|
|
37762
38393
|
function createWorkspaceFactory(options = {}) {
|
|
37763
38394
|
const { sandbox: sandboxConfig, github, fleet } = options;
|
|
37764
38395
|
const isLocalSandbox = sandboxConfig?.machine instanceof LocalSandbox;
|
|
38396
|
+
const githubTokenInjectors = /* @__PURE__ */ new Map();
|
|
37765
38397
|
return async ({ requestContext, mastra, skillExtension }) => {
|
|
37766
38398
|
const effectiveSkillExtension = skillExtension ?? factorySkillExtension;
|
|
37767
38399
|
const ctx = requestContext.get("controller");
|
|
@@ -37813,6 +38445,8 @@ function createWorkspaceFactory(options = {}) {
|
|
|
37813
38445
|
const existing = mastra?.getWorkspaceById(workspaceId);
|
|
37814
38446
|
if (existing) {
|
|
37815
38447
|
existing.setToolsConfig(MASTRACODE_WORKSPACE_TOOLS);
|
|
38448
|
+
const injectGithubToken3 = githubTokenInjectors.get(workspaceId);
|
|
38449
|
+
if (injectGithubToken3) registerGithubTokenInjector(requestContext, injectGithubToken3);
|
|
37816
38450
|
return existing;
|
|
37817
38451
|
}
|
|
37818
38452
|
} catch {
|
|
@@ -37843,6 +38477,14 @@ function createWorkspaceFactory(options = {}) {
|
|
|
37843
38477
|
repoFullName
|
|
37844
38478
|
});
|
|
37845
38479
|
if (projectRepository.setupCommand) await runWorktreeSetup(sandbox, workdir, projectRepository.setupCommand);
|
|
38480
|
+
const injectGithubToken2 = (freshToken) => {
|
|
38481
|
+
if (!sandbox.setEnvironmentVariable) {
|
|
38482
|
+
throw new Error("The active sandbox provider does not support runtime GitHub token refresh.");
|
|
38483
|
+
}
|
|
38484
|
+
sandbox.setEnvironmentVariable("GH_TOKEN", freshToken);
|
|
38485
|
+
};
|
|
38486
|
+
githubTokenInjectors.set(workspaceId, injectGithubToken2);
|
|
38487
|
+
registerGithubTokenInjector(requestContext, injectGithubToken2);
|
|
37846
38488
|
const filesystem = new SandboxFilesystem({ sandbox, workdir });
|
|
37847
38489
|
const projectSkillPaths = [path2.join(configDir, "skills"), ".claude/skills", ".agents/skills"];
|
|
37848
38490
|
const skillPaths = [...effectiveSkillExtension?.paths ?? [], ...projectSkillPaths];
|
|
@@ -37950,6 +38592,8 @@ var MastraFactory = class {
|
|
|
37950
38592
|
const workItemsStorage = storage.registerDomain(new WorkItemsStorage());
|
|
37951
38593
|
const modelCredentialsStorage = storage.registerDomain(new ModelCredentialsStorage());
|
|
37952
38594
|
const modelPacksStorage = storage.registerDomain(new ModelPacksStorage());
|
|
38595
|
+
const memorySettingsStorage = storage.registerDomain(new MemorySettingsStorage());
|
|
38596
|
+
const customProvidersStorage = storage.registerDomain(new CustomProvidersStorage());
|
|
37953
38597
|
const queueHealthStorage = storage.registerDomain(new QueueHealthStorage());
|
|
37954
38598
|
const integrationStorage = storage.registerDomain(new IntegrationStorage());
|
|
37955
38599
|
const factoryProjectsStorage = storage.registerDomain(new FactoryProjectsStorage());
|
|
@@ -37958,6 +38602,8 @@ var MastraFactory = class {
|
|
|
37958
38602
|
intake: intakeStorage,
|
|
37959
38603
|
modelCredentials: modelCredentialsStorage,
|
|
37960
38604
|
modelPacks: modelPacksStorage,
|
|
38605
|
+
memorySettings: memorySettingsStorage,
|
|
38606
|
+
customProviders: customProvidersStorage,
|
|
37961
38607
|
projects: factoryProjectsStorage,
|
|
37962
38608
|
queueHealth: queueHealthStorage,
|
|
37963
38609
|
workItems: workItemsStorage
|
|
@@ -38006,6 +38652,7 @@ var MastraFactory = class {
|
|
|
38006
38652
|
if (auth) {
|
|
38007
38653
|
registerTenantCredentialResolver(modelCredentialsStorage);
|
|
38008
38654
|
}
|
|
38655
|
+
registerCustomProvidersSource({ storage: customProvidersStorage, authEnabled: Boolean(auth) });
|
|
38009
38656
|
for (const integration of integrations) {
|
|
38010
38657
|
integration.initialize?.({
|
|
38011
38658
|
storage: integrationStorage.forIntegration(integration.id),
|
|
@@ -38059,6 +38706,13 @@ var MastraFactory = class {
|
|
|
38059
38706
|
);
|
|
38060
38707
|
}
|
|
38061
38708
|
}
|
|
38709
|
+
const mastraStorageBackend = (() => {
|
|
38710
|
+
for (let proto = Object.getPrototypeOf(storage); proto; proto = Object.getPrototypeOf(proto)) {
|
|
38711
|
+
if (proto.constructor?.name === "PgFactoryStorage") return "pg";
|
|
38712
|
+
if (proto.constructor?.name === "LibSQLFactoryStorage") return "libsql";
|
|
38713
|
+
}
|
|
38714
|
+
return void 0;
|
|
38715
|
+
})();
|
|
38062
38716
|
const toolIntegrations = integrationRegistrations.filter(
|
|
38063
38717
|
({ integration }) => integration.agentTools || integration.sessionTools
|
|
38064
38718
|
);
|
|
@@ -38070,7 +38724,11 @@ var MastraFactory = class {
|
|
|
38070
38724
|
fleet
|
|
38071
38725
|
}),
|
|
38072
38726
|
disableGithubSignals: true,
|
|
38727
|
+
// Memory settings live in the factory's `memory-settings` app table (per
|
|
38728
|
+
// org/user), so the host machine's TUI settings.json must not seed them.
|
|
38729
|
+
disableSettingsOmSeed: true,
|
|
38073
38730
|
storage: storage.getMastraStorage(),
|
|
38731
|
+
...mastraStorageBackend ? { storageBackend: mastraStorageBackend } : {},
|
|
38074
38732
|
...factoryProcessor ? { inputProcessors: [factoryProcessor] } : {},
|
|
38075
38733
|
...vector ? { vector } : {},
|
|
38076
38734
|
...toolIntegrations.length > 0 || workItemsStorage && transitionService ? {
|
|
@@ -38178,13 +38836,25 @@ var MastraFactory = class {
|
|
|
38178
38836
|
const uiDist = resolveUiDistDir();
|
|
38179
38837
|
const spa = uiDist ? [createSpaStaticMiddleware(uiDist)] : [];
|
|
38180
38838
|
if (!auth) {
|
|
38181
|
-
return {
|
|
38839
|
+
return {
|
|
38840
|
+
middleware: [
|
|
38841
|
+
createCustomProvidersPrimer({ auth: routeAuth, storage: customProvidersStorage, authEnabled: false }),
|
|
38842
|
+
...spa
|
|
38843
|
+
],
|
|
38844
|
+
...cors,
|
|
38845
|
+
...onError
|
|
38846
|
+
};
|
|
38182
38847
|
}
|
|
38183
38848
|
return {
|
|
38184
38849
|
auth,
|
|
38185
38850
|
middleware: [
|
|
38186
38851
|
createFactoryAuthGate(auth),
|
|
38187
38852
|
createTenantCredentialPrimer({ auth: routeAuth, credentials: modelCredentialsStorage }),
|
|
38853
|
+
createCustomProvidersPrimer({
|
|
38854
|
+
auth: routeAuth,
|
|
38855
|
+
storage: customProvidersStorage,
|
|
38856
|
+
authEnabled: Boolean(auth)
|
|
38857
|
+
}),
|
|
38188
38858
|
...spa
|
|
38189
38859
|
],
|
|
38190
38860
|
...cors,
|