@mastra/factory 0.1.0-alpha.3 → 0.1.0-alpha.4
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 +33 -0
- package/dist/factory.d.ts.map +1 -1
- package/dist/factory.js +772 -206
- package/dist/factory.js.map +1 -1
- package/dist/index.js +772 -206
- package/dist/index.js.map +1 -1
- package/dist/integrations/github/integration.d.ts +2 -2
- package/dist/integrations/github/integration.js +43 -11
- package/dist/integrations/github/integration.js.map +1 -1
- package/dist/integrations/github/provenance.js.map +1 -1
- package/dist/integrations/github/routes.js +10 -9
- 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/github/integration.js +41 -9
- package/dist/integrations/platform/github/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 +7 -6
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,
|
|
@@ -29076,6 +29148,158 @@ function isNotFound2(error) {
|
|
|
29076
29148
|
return error instanceof PlatformApiError && error.status === 404;
|
|
29077
29149
|
}
|
|
29078
29150
|
|
|
29151
|
+
// src/routes/custom-provider-source.ts
|
|
29152
|
+
import { setCustomProvidersSource } from "@mastra/code-sdk/agents/custom-provider-source";
|
|
29153
|
+
|
|
29154
|
+
// src/routes/provider-credentials.ts
|
|
29155
|
+
function getAuthProviderId(provider) {
|
|
29156
|
+
return provider === "openai" ? "openai-codex" : provider;
|
|
29157
|
+
}
|
|
29158
|
+
var WEB_OAUTH_FLOW_KINDS = {
|
|
29159
|
+
anthropic: "paste-code",
|
|
29160
|
+
openai: "device-code",
|
|
29161
|
+
"github-copilot": "device-code",
|
|
29162
|
+
xai: "device-code"
|
|
29163
|
+
};
|
|
29164
|
+
async function getTenantCredentialsStorage(credentials) {
|
|
29165
|
+
if (!credentials) return void 0;
|
|
29166
|
+
try {
|
|
29167
|
+
await credentials.ensureReady();
|
|
29168
|
+
} catch {
|
|
29169
|
+
return void 0;
|
|
29170
|
+
}
|
|
29171
|
+
return credentials;
|
|
29172
|
+
}
|
|
29173
|
+
function tenantOrgId(tenant) {
|
|
29174
|
+
return tenant.orgId ?? `user:${tenant.userId}`;
|
|
29175
|
+
}
|
|
29176
|
+
async function resolveCredentialContext({
|
|
29177
|
+
c,
|
|
29178
|
+
auth,
|
|
29179
|
+
credentials
|
|
29180
|
+
}) {
|
|
29181
|
+
await auth.ensureUser(c);
|
|
29182
|
+
const tenant = auth.tenant(c);
|
|
29183
|
+
if (!tenant) {
|
|
29184
|
+
if (auth.enabled()) return { response: c.json({ error: "unauthorized" }, 401) };
|
|
29185
|
+
return { mode: "local" };
|
|
29186
|
+
}
|
|
29187
|
+
const storage = await getTenantCredentialsStorage(credentials);
|
|
29188
|
+
if (!storage) {
|
|
29189
|
+
return {
|
|
29190
|
+
response: c.json(
|
|
29191
|
+
{
|
|
29192
|
+
error: "credentials_unavailable",
|
|
29193
|
+
message: "Tenant credential storage is unavailable \u2014 the app database is not configured or failed to start."
|
|
29194
|
+
},
|
|
29195
|
+
503
|
|
29196
|
+
)
|
|
29197
|
+
};
|
|
29198
|
+
}
|
|
29199
|
+
return { mode: "tenant", storage, orgId: tenantOrgId(tenant), userId: tenant.userId };
|
|
29200
|
+
}
|
|
29201
|
+
async function listTenantCredentialsForRequest({
|
|
29202
|
+
c,
|
|
29203
|
+
auth,
|
|
29204
|
+
credentials
|
|
29205
|
+
}) {
|
|
29206
|
+
await auth.ensureUser(c);
|
|
29207
|
+
const tenant = auth.tenant(c);
|
|
29208
|
+
if (!tenant) return auth.enabled() ? [] : void 0;
|
|
29209
|
+
const storage = await getTenantCredentialsStorage(credentials);
|
|
29210
|
+
if (!storage) return [];
|
|
29211
|
+
return storage.listCredentials(tenantOrgId(tenant), tenant.userId);
|
|
29212
|
+
}
|
|
29213
|
+
|
|
29214
|
+
// src/routes/custom-provider-source.ts
|
|
29215
|
+
var SNAPSHOT_TTL_MS = 15e3;
|
|
29216
|
+
var MAX_CACHED_ORGS = 1e3;
|
|
29217
|
+
var LOCAL_ORG = "local";
|
|
29218
|
+
var OrgCustomProvidersSnapshot = class {
|
|
29219
|
+
#orgId;
|
|
29220
|
+
#storage;
|
|
29221
|
+
#snapshot = [];
|
|
29222
|
+
#fetchedAt = 0;
|
|
29223
|
+
#hydrating;
|
|
29224
|
+
constructor(orgId, storage) {
|
|
29225
|
+
this.#orgId = orgId;
|
|
29226
|
+
this.#storage = storage;
|
|
29227
|
+
}
|
|
29228
|
+
/** Hydrate the snapshot when stale; coalesces concurrent callers. */
|
|
29229
|
+
async ensureFresh(now = Date.now()) {
|
|
29230
|
+
if (now - this.#fetchedAt < SNAPSHOT_TTL_MS) return;
|
|
29231
|
+
this.#hydrating ??= this.#hydrate().finally(() => {
|
|
29232
|
+
this.#hydrating = void 0;
|
|
29233
|
+
});
|
|
29234
|
+
await this.#hydrating;
|
|
29235
|
+
}
|
|
29236
|
+
async #hydrate() {
|
|
29237
|
+
await this.#storage.ensureReady();
|
|
29238
|
+
const records = await this.#storage.list({ orgId: this.#orgId });
|
|
29239
|
+
this.#snapshot = records.map((record) => ({
|
|
29240
|
+
name: record.name,
|
|
29241
|
+
url: record.url,
|
|
29242
|
+
apiKey: record.apiKey ?? void 0,
|
|
29243
|
+
models: record.models
|
|
29244
|
+
}));
|
|
29245
|
+
this.#fetchedAt = Date.now();
|
|
29246
|
+
}
|
|
29247
|
+
/** Sync by contract; kicks a background re-hydrate when the snapshot is stale. */
|
|
29248
|
+
get() {
|
|
29249
|
+
if (Date.now() - this.#fetchedAt >= SNAPSHOT_TTL_MS) {
|
|
29250
|
+
void this.ensureFresh().catch(() => {
|
|
29251
|
+
});
|
|
29252
|
+
}
|
|
29253
|
+
return this.#snapshot;
|
|
29254
|
+
}
|
|
29255
|
+
};
|
|
29256
|
+
var orgSnapshots = /* @__PURE__ */ new Map();
|
|
29257
|
+
function snapshotFor(orgId, storage) {
|
|
29258
|
+
let snapshot = orgSnapshots.get(orgId);
|
|
29259
|
+
if (!snapshot) {
|
|
29260
|
+
if (orgSnapshots.size >= MAX_CACHED_ORGS) {
|
|
29261
|
+
const oldest = orgSnapshots.keys().next().value;
|
|
29262
|
+
if (oldest !== void 0) orgSnapshots.delete(oldest);
|
|
29263
|
+
}
|
|
29264
|
+
snapshot = new OrgCustomProvidersSnapshot(orgId, storage);
|
|
29265
|
+
orgSnapshots.set(orgId, snapshot);
|
|
29266
|
+
}
|
|
29267
|
+
return snapshot;
|
|
29268
|
+
}
|
|
29269
|
+
function orgForTenant(tenant, authEnabled) {
|
|
29270
|
+
if (!authEnabled) return LOCAL_ORG;
|
|
29271
|
+
return tenant ? tenantOrgId(tenant) : void 0;
|
|
29272
|
+
}
|
|
29273
|
+
function registerCustomProvidersSource({
|
|
29274
|
+
storage,
|
|
29275
|
+
authEnabled
|
|
29276
|
+
}) {
|
|
29277
|
+
setCustomProvidersSource((tenant) => {
|
|
29278
|
+
const orgId = orgForTenant(tenant, authEnabled);
|
|
29279
|
+
if (!orgId) return [];
|
|
29280
|
+
return snapshotFor(orgId, storage).get();
|
|
29281
|
+
});
|
|
29282
|
+
}
|
|
29283
|
+
function invalidateCustomProvidersSnapshots(tenant) {
|
|
29284
|
+
orgSnapshots.delete(tenant.orgId);
|
|
29285
|
+
}
|
|
29286
|
+
function createCustomProvidersPrimer({
|
|
29287
|
+
auth,
|
|
29288
|
+
storage,
|
|
29289
|
+
authEnabled
|
|
29290
|
+
}) {
|
|
29291
|
+
return async (c, next) => {
|
|
29292
|
+
const orgId = orgForTenant(auth.tenant(c), authEnabled);
|
|
29293
|
+
if (orgId) {
|
|
29294
|
+
try {
|
|
29295
|
+
await snapshotFor(orgId, storage).ensureFresh();
|
|
29296
|
+
} catch {
|
|
29297
|
+
}
|
|
29298
|
+
}
|
|
29299
|
+
await next();
|
|
29300
|
+
};
|
|
29301
|
+
}
|
|
29302
|
+
|
|
29079
29303
|
// src/routes/projects.ts
|
|
29080
29304
|
import { registerApiRoute as registerApiRoute6 } from "@mastra/core/server";
|
|
29081
29305
|
|
|
@@ -30503,82 +30727,10 @@ var FactoryTransitionService = class {
|
|
|
30503
30727
|
};
|
|
30504
30728
|
|
|
30505
30729
|
// 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";
|
|
30730
|
+
import { DEFAULT_OM_MODEL_ID } from "@mastra/code-sdk/constants";
|
|
30512
30731
|
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";
|
|
30732
|
+
import { getCustomProviderId, THREAD_ACTIVE_MODEL_PACK_ID_KEY } from "@mastra/code-sdk/onboarding/settings";
|
|
30519
30733
|
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
30734
|
function loose6(c) {
|
|
30583
30735
|
return c;
|
|
30584
30736
|
}
|
|
@@ -30622,15 +30774,39 @@ async function listProviders({
|
|
|
30622
30774
|
}
|
|
30623
30775
|
return Array.from(seen.values()).sort((a, b) => a.provider.localeCompare(b.provider));
|
|
30624
30776
|
}
|
|
30625
|
-
function
|
|
30626
|
-
|
|
30627
|
-
|
|
30628
|
-
|
|
30629
|
-
|
|
30630
|
-
|
|
30631
|
-
|
|
30632
|
-
|
|
30633
|
-
|
|
30777
|
+
function toCustomProviderInfo(record) {
|
|
30778
|
+
return {
|
|
30779
|
+
id: record.providerId,
|
|
30780
|
+
name: record.name,
|
|
30781
|
+
url: record.url,
|
|
30782
|
+
hasApiKey: Boolean(record.apiKey),
|
|
30783
|
+
models: record.models
|
|
30784
|
+
};
|
|
30785
|
+
}
|
|
30786
|
+
async function resolveCustomProvidersContext({
|
|
30787
|
+
c,
|
|
30788
|
+
auth,
|
|
30789
|
+
customProviders
|
|
30790
|
+
}) {
|
|
30791
|
+
await auth.ensureUser(c);
|
|
30792
|
+
const tenant = auth.tenant(c);
|
|
30793
|
+
if (!tenant && auth.enabled()) return { response: c.json({ error: "unauthorized" }, 401) };
|
|
30794
|
+
if (customProviders) {
|
|
30795
|
+
try {
|
|
30796
|
+
await customProviders.ensureReady();
|
|
30797
|
+
return tenant ? { storage: customProviders, orgId: tenantOrgId(tenant), userId: tenant.userId } : { storage: customProviders, orgId: "local", userId: "local" };
|
|
30798
|
+
} catch {
|
|
30799
|
+
}
|
|
30800
|
+
}
|
|
30801
|
+
return {
|
|
30802
|
+
response: c.json(
|
|
30803
|
+
{
|
|
30804
|
+
error: "custom_providers_unavailable",
|
|
30805
|
+
message: "Custom provider storage is unavailable \u2014 the app database is not configured or failed to start."
|
|
30806
|
+
},
|
|
30807
|
+
503
|
|
30808
|
+
)
|
|
30809
|
+
};
|
|
30634
30810
|
}
|
|
30635
30811
|
function parseCustomProviderBody(body) {
|
|
30636
30812
|
if (!body || typeof body !== "object") return { error: "Invalid JSON body" };
|
|
@@ -30695,15 +30871,6 @@ async function buildProviderAccess({
|
|
|
30695
30871
|
function canUseModelProvider(access, provider) {
|
|
30696
30872
|
return Boolean(access[provider]);
|
|
30697
30873
|
}
|
|
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
30874
|
async function resolvePackContext({
|
|
30708
30875
|
c,
|
|
30709
30876
|
auth,
|
|
@@ -30711,23 +30878,23 @@ async function resolvePackContext({
|
|
|
30711
30878
|
}) {
|
|
30712
30879
|
await auth.ensureUser(c);
|
|
30713
30880
|
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
|
-
};
|
|
30881
|
+
if (!tenant && auth.enabled()) return { response: c.json({ error: "unauthorized" }, 401) };
|
|
30882
|
+
if (modelPacks) {
|
|
30883
|
+
try {
|
|
30884
|
+
await modelPacks.ensureReady();
|
|
30885
|
+
return tenant ? { storage: modelPacks, orgId: tenantOrgId(tenant), userId: tenant.userId } : { storage: modelPacks, orgId: "local", userId: "local" };
|
|
30886
|
+
} catch {
|
|
30887
|
+
}
|
|
30729
30888
|
}
|
|
30730
|
-
return {
|
|
30889
|
+
return {
|
|
30890
|
+
response: c.json(
|
|
30891
|
+
{
|
|
30892
|
+
error: "model_packs_unavailable",
|
|
30893
|
+
message: "Model pack storage is unavailable \u2014 the app database is not configured or failed to start."
|
|
30894
|
+
},
|
|
30895
|
+
503
|
|
30896
|
+
)
|
|
30897
|
+
};
|
|
30731
30898
|
}
|
|
30732
30899
|
function recordToModePack(record) {
|
|
30733
30900
|
return { id: `custom:${record.id}`, name: record.name, description: "Saved custom pack", models: record.models };
|
|
@@ -30740,7 +30907,7 @@ async function listModelPacks({
|
|
|
30740
30907
|
activePackId
|
|
30741
30908
|
}) {
|
|
30742
30909
|
const access = await buildProviderAccess({ controller, authStorage, tenantCredentials });
|
|
30743
|
-
const packs =
|
|
30910
|
+
const packs = [
|
|
30744
30911
|
...getAvailableModePacks(access),
|
|
30745
30912
|
...(await packContext.storage.list({ orgId: packContext.orgId })).map(recordToModePack)
|
|
30746
30913
|
];
|
|
@@ -30798,20 +30965,57 @@ function readOMConfig(session) {
|
|
|
30798
30965
|
observeAttachments: observeAttachments === true || observeAttachments === false ? observeAttachments : "auto"
|
|
30799
30966
|
};
|
|
30800
30967
|
}
|
|
30801
|
-
function
|
|
30802
|
-
|
|
30803
|
-
|
|
30804
|
-
|
|
30805
|
-
saveSettings(settings);
|
|
30806
|
-
}
|
|
30807
|
-
function persistOmRoleOverride({
|
|
30808
|
-
role,
|
|
30809
|
-
modelId,
|
|
30810
|
-
otherRoleCurrentModelId
|
|
30968
|
+
async function resolveMemorySettingsContext({
|
|
30969
|
+
c,
|
|
30970
|
+
auth,
|
|
30971
|
+
memorySettings
|
|
30811
30972
|
}) {
|
|
30812
|
-
|
|
30813
|
-
|
|
30814
|
-
|
|
30973
|
+
await auth.ensureUser(c);
|
|
30974
|
+
const tenant = auth.tenant(c);
|
|
30975
|
+
if (!tenant && auth.enabled()) return { response: c.json({ error: "unauthorized" }, 401) };
|
|
30976
|
+
if (memorySettings) {
|
|
30977
|
+
try {
|
|
30978
|
+
await memorySettings.ensureReady();
|
|
30979
|
+
return tenant ? { storage: memorySettings, orgId: tenantOrgId(tenant), userId: tenant.userId } : { storage: memorySettings, orgId: "local", userId: "local" };
|
|
30980
|
+
} catch {
|
|
30981
|
+
}
|
|
30982
|
+
}
|
|
30983
|
+
return {
|
|
30984
|
+
response: c.json(
|
|
30985
|
+
{
|
|
30986
|
+
error: "memory_settings_unavailable",
|
|
30987
|
+
message: "Memory settings storage is unavailable \u2014 the app database is not configured or failed to start."
|
|
30988
|
+
},
|
|
30989
|
+
503
|
|
30990
|
+
)
|
|
30991
|
+
};
|
|
30992
|
+
}
|
|
30993
|
+
async function persistMemorySettings(context, patch, fillIfUnset) {
|
|
30994
|
+
await context.storage.patch({ orgId: context.orgId, userId: context.userId, patch, fillIfUnset });
|
|
30995
|
+
}
|
|
30996
|
+
async function hydrateSessionMemorySettings(session, record) {
|
|
30997
|
+
for (const role of ["observer", "reflector"]) {
|
|
30998
|
+
const stored = role === "observer" ? record?.observerModelId : record?.reflectorModelId;
|
|
30999
|
+
const target = stored ?? DEFAULT_OM_MODEL_ID;
|
|
31000
|
+
if (session.om[role].modelId() !== target) {
|
|
31001
|
+
await session.om[role].switchModel({ modelId: target });
|
|
31002
|
+
}
|
|
31003
|
+
}
|
|
31004
|
+
const state = session.state.get() ?? {};
|
|
31005
|
+
const updates = {};
|
|
31006
|
+
const observationThreshold = record?.observationThreshold ?? DEFAULT_OBSERVATION_THRESHOLD;
|
|
31007
|
+
if (state.observationThreshold !== observationThreshold) {
|
|
31008
|
+
updates.observationThreshold = observationThreshold;
|
|
31009
|
+
}
|
|
31010
|
+
const reflectionThreshold = record?.reflectionThreshold ?? DEFAULT_REFLECTION_THRESHOLD;
|
|
31011
|
+
if (state.reflectionThreshold !== reflectionThreshold) {
|
|
31012
|
+
updates.reflectionThreshold = reflectionThreshold;
|
|
31013
|
+
}
|
|
31014
|
+
const observeAttachments = record?.observeAttachments ?? "auto";
|
|
31015
|
+
if ((state.observeAttachments ?? "auto") !== observeAttachments) {
|
|
31016
|
+
updates.observeAttachments = observeAttachments;
|
|
31017
|
+
}
|
|
31018
|
+
if (Object.keys(updates).length > 0) await session.state.set(updates);
|
|
30815
31019
|
}
|
|
30816
31020
|
var ConfigRoutes = class extends Route {
|
|
30817
31021
|
routes() {
|
|
@@ -30819,6 +31023,8 @@ var ConfigRoutes = class extends Route {
|
|
|
30819
31023
|
const { controller, authStorage, auth } = options;
|
|
30820
31024
|
const onCredentialsChanged = options.onCredentialsChanged ?? (() => {
|
|
30821
31025
|
});
|
|
31026
|
+
const onCustomProvidersChanged = options.onCustomProvidersChanged ?? (() => {
|
|
31027
|
+
});
|
|
30822
31028
|
return [
|
|
30823
31029
|
registerApiRoute7("/web/config/providers", {
|
|
30824
31030
|
method: "GET",
|
|
@@ -30910,14 +31116,22 @@ var ConfigRoutes = class extends Route {
|
|
|
30910
31116
|
}
|
|
30911
31117
|
}),
|
|
30912
31118
|
// ── Custom providers (OpenAI-compatible endpoints) ──────────────────────
|
|
30913
|
-
// Mirrors the TUI's /custom-providers command
|
|
30914
|
-
// (
|
|
31119
|
+
// Mirrors the TUI's /custom-providers command, but backed by the
|
|
31120
|
+
// `custom-providers` domain (org rows in tenant mode, a sentinel `local`
|
|
31121
|
+
// org in no-auth mode) — the server never reads settings.json for these.
|
|
30915
31122
|
registerApiRoute7("/web/config/custom-providers", {
|
|
30916
31123
|
method: "GET",
|
|
30917
31124
|
requiresAuth: false,
|
|
30918
|
-
handler: (c) => {
|
|
31125
|
+
handler: async (c) => {
|
|
31126
|
+
const ctx = await resolveCustomProvidersContext({
|
|
31127
|
+
c: loose6(c),
|
|
31128
|
+
auth,
|
|
31129
|
+
customProviders: options.customProviders
|
|
31130
|
+
});
|
|
31131
|
+
if ("response" in ctx) return ctx.response;
|
|
30919
31132
|
try {
|
|
30920
|
-
|
|
31133
|
+
const records = await ctx.storage.list({ orgId: ctx.orgId });
|
|
31134
|
+
return c.json({ providers: records.map(toCustomProviderInfo) });
|
|
30921
31135
|
} catch (error) {
|
|
30922
31136
|
return c.json({ error: error instanceof Error ? error.message : String(error) }, 500);
|
|
30923
31137
|
}
|
|
@@ -30927,6 +31141,12 @@ var ConfigRoutes = class extends Route {
|
|
|
30927
31141
|
method: "POST",
|
|
30928
31142
|
requiresAuth: false,
|
|
30929
31143
|
handler: async (c) => {
|
|
31144
|
+
const ctx = await resolveCustomProvidersContext({
|
|
31145
|
+
c: loose6(c),
|
|
31146
|
+
auth,
|
|
31147
|
+
customProviders: options.customProviders
|
|
31148
|
+
});
|
|
31149
|
+
if ("response" in ctx) return ctx.response;
|
|
30930
31150
|
let body;
|
|
30931
31151
|
try {
|
|
30932
31152
|
body = await c.req.json();
|
|
@@ -30937,11 +31157,20 @@ var ConfigRoutes = class extends Route {
|
|
|
30937
31157
|
if ("error" in parsed) return c.json({ error: parsed.error }, 400);
|
|
30938
31158
|
const previousId = body && typeof body === "object" && typeof body.previousId === "string" ? body.previousId : void 0;
|
|
30939
31159
|
try {
|
|
30940
|
-
const
|
|
30941
|
-
|
|
30942
|
-
|
|
30943
|
-
|
|
30944
|
-
|
|
31160
|
+
const record = await ctx.storage.upsert({
|
|
31161
|
+
orgId: ctx.orgId,
|
|
31162
|
+
userId: ctx.userId,
|
|
31163
|
+
input: {
|
|
31164
|
+
providerId: getCustomProviderId(parsed.name),
|
|
31165
|
+
name: parsed.name,
|
|
31166
|
+
url: parsed.url,
|
|
31167
|
+
apiKey: parsed.apiKey,
|
|
31168
|
+
models: parsed.models
|
|
31169
|
+
},
|
|
31170
|
+
previousProviderId: previousId
|
|
31171
|
+
});
|
|
31172
|
+
onCustomProvidersChanged({ orgId: ctx.orgId });
|
|
31173
|
+
return c.json({ ok: true, provider: toCustomProviderInfo(record) });
|
|
30945
31174
|
} catch (error) {
|
|
30946
31175
|
return c.json({ error: error instanceof Error ? error.message : String(error) }, 500);
|
|
30947
31176
|
}
|
|
@@ -30950,12 +31179,17 @@ var ConfigRoutes = class extends Route {
|
|
|
30950
31179
|
registerApiRoute7("/web/config/custom-providers/:id", {
|
|
30951
31180
|
method: "DELETE",
|
|
30952
31181
|
requiresAuth: false,
|
|
30953
|
-
handler: (c) => {
|
|
31182
|
+
handler: async (c) => {
|
|
31183
|
+
const ctx = await resolveCustomProvidersContext({
|
|
31184
|
+
c: loose6(c),
|
|
31185
|
+
auth,
|
|
31186
|
+
customProviders: options.customProviders
|
|
31187
|
+
});
|
|
31188
|
+
if ("response" in ctx) return ctx.response;
|
|
30954
31189
|
const id = c.req.param("id");
|
|
30955
31190
|
try {
|
|
30956
|
-
|
|
30957
|
-
|
|
30958
|
-
saveSettings(settings);
|
|
31191
|
+
await ctx.storage.delete({ orgId: ctx.orgId, providerId: id });
|
|
31192
|
+
onCustomProvidersChanged({ orgId: ctx.orgId });
|
|
30959
31193
|
return c.json({ ok: true });
|
|
30960
31194
|
} catch (error) {
|
|
30961
31195
|
return c.json({ error: error instanceof Error ? error.message : String(error) }, 500);
|
|
@@ -30984,8 +31218,30 @@ var ConfigRoutes = class extends Route {
|
|
|
30984
31218
|
tenantCredentials
|
|
30985
31219
|
})
|
|
30986
31220
|
]);
|
|
31221
|
+
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 }));
|
|
31222
|
+
if (options.customProviders) {
|
|
31223
|
+
try {
|
|
31224
|
+
const ctx = await resolveCustomProvidersContext({
|
|
31225
|
+
c: loose6(c),
|
|
31226
|
+
auth,
|
|
31227
|
+
customProviders: options.customProviders
|
|
31228
|
+
});
|
|
31229
|
+
if (!("response" in ctx)) {
|
|
31230
|
+
const known = new Set(catalog.map((m) => m.id));
|
|
31231
|
+
for (const record of await ctx.storage.list({ orgId: ctx.orgId })) {
|
|
31232
|
+
for (const model of record.models) {
|
|
31233
|
+
const id = `${record.providerId}/${model}`;
|
|
31234
|
+
if (known.has(id)) continue;
|
|
31235
|
+
known.add(id);
|
|
31236
|
+
catalog.push({ id, provider: record.providerId, modelName: model, hasApiKey: true });
|
|
31237
|
+
}
|
|
31238
|
+
}
|
|
31239
|
+
}
|
|
31240
|
+
} catch {
|
|
31241
|
+
}
|
|
31242
|
+
}
|
|
30987
31243
|
return c.json({
|
|
30988
|
-
models:
|
|
31244
|
+
models: catalog.sort(
|
|
30989
31245
|
(a, b) => a.provider === b.provider ? a.id.localeCompare(b.id) : a.provider.localeCompare(b.provider)
|
|
30990
31246
|
)
|
|
30991
31247
|
});
|
|
@@ -30995,10 +31251,10 @@ var ConfigRoutes = class extends Route {
|
|
|
30995
31251
|
}
|
|
30996
31252
|
}),
|
|
30997
31253
|
// ── Model packs ─────────────────────────────────────────────────────────
|
|
30998
|
-
// Mirrors the TUI's /models-pack command. Custom-pack CRUD
|
|
30999
|
-
//
|
|
31000
|
-
//
|
|
31001
|
-
// the controller registry by resourceId.
|
|
31254
|
+
// Mirrors the TUI's /models-pack command. Custom-pack CRUD lives in the
|
|
31255
|
+
// model-packs storage domain (org-scoped, sentinel `local` org in no-auth
|
|
31256
|
+
// mode — never settings.json); activation is session-scoped and resolves
|
|
31257
|
+
// the session from the controller registry by resourceId.
|
|
31002
31258
|
registerApiRoute7("/web/config/model-packs", {
|
|
31003
31259
|
method: "GET",
|
|
31004
31260
|
requiresAuth: false,
|
|
@@ -31052,21 +31308,12 @@ var ConfigRoutes = class extends Route {
|
|
|
31052
31308
|
return c.json({ error: "models.build, models.plan and models.fast are required" }, 400);
|
|
31053
31309
|
}
|
|
31054
31310
|
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 } } });
|
|
31311
|
+
const record = await packContext.storage.upsert({
|
|
31312
|
+
orgId: packContext.orgId,
|
|
31313
|
+
userId: packContext.userId,
|
|
31314
|
+
input: { name, models: { build, plan, fast } }
|
|
31315
|
+
});
|
|
31316
|
+
return c.json({ ok: true, pack: recordToModePack(record) });
|
|
31070
31317
|
} catch (error) {
|
|
31071
31318
|
return c.json({ error: error instanceof Error ? error.message : String(error) }, 500);
|
|
31072
31319
|
}
|
|
@@ -31080,15 +31327,9 @@ var ConfigRoutes = class extends Route {
|
|
|
31080
31327
|
if ("response" in packContext) return packContext.response;
|
|
31081
31328
|
const id = decodeURIComponent(c.req.param("id"));
|
|
31082
31329
|
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 });
|
|
31330
|
+
const recordId = id.startsWith("custom:") ? id.slice("custom:".length) : id;
|
|
31331
|
+
const deleted = await packContext.storage.delete({ orgId: packContext.orgId, id: recordId });
|
|
31332
|
+
return deleted ? c.json({ ok: true }) : c.json({ error: `Unknown pack "${id}"` }, 404);
|
|
31092
31333
|
} catch (error) {
|
|
31093
31334
|
return c.json({ error: error instanceof Error ? error.message : String(error) }, 500);
|
|
31094
31335
|
}
|
|
@@ -31135,8 +31376,10 @@ var ConfigRoutes = class extends Route {
|
|
|
31135
31376
|
}),
|
|
31136
31377
|
// ── Observational memory ──────────────────────────────────────────────────
|
|
31137
31378
|
// Mirrors the TUI's /om command. All five knobs are session-scoped (resolved
|
|
31138
|
-
// from the session, persisted to its state + thread setting)
|
|
31139
|
-
//
|
|
31379
|
+
// from the session, persisted to its state + thread setting) and durably
|
|
31380
|
+
// stored in the per-(org, user) `memory-settings` app table — never
|
|
31381
|
+
// settings.json. GET hydrates the session from the stored row first so the
|
|
31382
|
+
// DB, not the SDK's boot-time seed, is the source of truth.
|
|
31140
31383
|
registerApiRoute7("/web/config/om", {
|
|
31141
31384
|
method: "GET",
|
|
31142
31385
|
requiresAuth: false,
|
|
@@ -31144,9 +31387,17 @@ var ConfigRoutes = class extends Route {
|
|
|
31144
31387
|
const resourceId = c.req.query("resourceId");
|
|
31145
31388
|
const scope = c.req.query("scope") || void 0;
|
|
31146
31389
|
if (!resourceId) return c.json({ error: "Missing required query param: resourceId" }, 400);
|
|
31390
|
+
const context = await resolveMemorySettingsContext({
|
|
31391
|
+
c: loose6(c),
|
|
31392
|
+
auth,
|
|
31393
|
+
memorySettings: options.memorySettings
|
|
31394
|
+
});
|
|
31395
|
+
if ("response" in context) return context.response;
|
|
31147
31396
|
try {
|
|
31148
31397
|
const session = await controller.getSessionByResource?.(resourceId, scope);
|
|
31149
31398
|
if (!session) return c.json({ error: `No session for resourceId "${resourceId}"` }, 404);
|
|
31399
|
+
const record = await context.storage.get({ orgId: context.orgId, userId: context.userId });
|
|
31400
|
+
await hydrateSessionMemorySettings(session, record);
|
|
31150
31401
|
return c.json({ config: readOMConfig(session) });
|
|
31151
31402
|
} catch (error) {
|
|
31152
31403
|
return c.json({ error: error instanceof Error ? error.message : String(error) }, 500);
|
|
@@ -31172,13 +31423,24 @@ var ConfigRoutes = class extends Route {
|
|
|
31172
31423
|
const modelId = typeof body.modelId === "string" ? body.modelId.trim() : "";
|
|
31173
31424
|
if (!resourceId) return c.json({ error: "Missing required field: resourceId" }, 400);
|
|
31174
31425
|
if (!modelId) return c.json({ error: "Missing required field: modelId" }, 400);
|
|
31426
|
+
const context = await resolveMemorySettingsContext({
|
|
31427
|
+
c: loose6(c),
|
|
31428
|
+
auth,
|
|
31429
|
+
memorySettings: options.memorySettings
|
|
31430
|
+
});
|
|
31431
|
+
if ("response" in context) return context.response;
|
|
31175
31432
|
try {
|
|
31176
31433
|
const session = await controller.getSessionByResource?.(resourceId, scope);
|
|
31177
31434
|
if (!session) return c.json({ error: `No session for resourceId "${resourceId}"` }, 404);
|
|
31178
31435
|
const otherRole = role === "observer" ? session.om.reflector : session.om.observer;
|
|
31179
31436
|
const otherRoleCurrentModelId = otherRole.modelId() ?? null;
|
|
31180
31437
|
await session.om[role].switchModel({ modelId });
|
|
31181
|
-
|
|
31438
|
+
const otherKey = role === "observer" ? "reflectorModelId" : "observerModelId";
|
|
31439
|
+
await persistMemorySettings(
|
|
31440
|
+
context,
|
|
31441
|
+
{ [role === "observer" ? "observerModelId" : "reflectorModelId"]: modelId },
|
|
31442
|
+
otherRoleCurrentModelId ? { [otherKey]: otherRoleCurrentModelId } : void 0
|
|
31443
|
+
);
|
|
31182
31444
|
return c.json({ ok: true, config: readOMConfig(session) });
|
|
31183
31445
|
} catch (error) {
|
|
31184
31446
|
return c.json({ error: error instanceof Error ? error.message : String(error) }, 500);
|
|
@@ -31203,19 +31465,27 @@ var ConfigRoutes = class extends Route {
|
|
|
31203
31465
|
if (observation === void 0 && reflection === void 0) {
|
|
31204
31466
|
return c.json({ error: "Provide observationThreshold and/or reflectionThreshold (positive numbers)" }, 400);
|
|
31205
31467
|
}
|
|
31468
|
+
const context = await resolveMemorySettingsContext({
|
|
31469
|
+
c: loose6(c),
|
|
31470
|
+
auth,
|
|
31471
|
+
memorySettings: options.memorySettings
|
|
31472
|
+
});
|
|
31473
|
+
if ("response" in context) return context.response;
|
|
31206
31474
|
try {
|
|
31207
31475
|
const session = await controller.getSessionByResource?.(resourceId, scope);
|
|
31208
31476
|
if (!session) return c.json({ error: `No session for resourceId "${resourceId}"` }, 404);
|
|
31209
31477
|
if (observation !== void 0) {
|
|
31210
31478
|
await session.state.set({ observationThreshold: observation });
|
|
31211
31479
|
await session.thread.setSetting({ key: "observationThreshold", value: observation });
|
|
31212
|
-
persistOmThreshold({ role: "observation", value: observation });
|
|
31213
31480
|
}
|
|
31214
31481
|
if (reflection !== void 0) {
|
|
31215
31482
|
await session.state.set({ reflectionThreshold: reflection });
|
|
31216
31483
|
await session.thread.setSetting({ key: "reflectionThreshold", value: reflection });
|
|
31217
|
-
persistOmThreshold({ role: "reflection", value: reflection });
|
|
31218
31484
|
}
|
|
31485
|
+
await persistMemorySettings(context, {
|
|
31486
|
+
...observation !== void 0 ? { observationThreshold: observation } : {},
|
|
31487
|
+
...reflection !== void 0 ? { reflectionThreshold: reflection } : {}
|
|
31488
|
+
});
|
|
31219
31489
|
return c.json({ ok: true, config: readOMConfig(session) });
|
|
31220
31490
|
} catch (error) {
|
|
31221
31491
|
return c.json({ error: error instanceof Error ? error.message : String(error) }, 500);
|
|
@@ -31240,12 +31510,18 @@ var ConfigRoutes = class extends Route {
|
|
|
31240
31510
|
if (raw !== "auto" && raw !== true && raw !== false) {
|
|
31241
31511
|
return c.json({ error: "value must be 'auto', true, or false" }, 400);
|
|
31242
31512
|
}
|
|
31513
|
+
const context = await resolveMemorySettingsContext({
|
|
31514
|
+
c: loose6(c),
|
|
31515
|
+
auth,
|
|
31516
|
+
memorySettings: options.memorySettings
|
|
31517
|
+
});
|
|
31518
|
+
if ("response" in context) return context.response;
|
|
31243
31519
|
try {
|
|
31244
31520
|
const session = await controller.getSessionByResource?.(resourceId, scope);
|
|
31245
31521
|
if (!session) return c.json({ error: `No session for resourceId "${resourceId}"` }, 404);
|
|
31246
31522
|
await session.state.set({ observeAttachments: value });
|
|
31247
31523
|
await session.thread.setSetting({ key: "observeAttachments", value });
|
|
31248
|
-
|
|
31524
|
+
await persistMemorySettings(context, { observeAttachments: value });
|
|
31249
31525
|
return c.json({ ok: true, config: readOMConfig(session) });
|
|
31250
31526
|
} catch (error) {
|
|
31251
31527
|
return c.json({ error: error instanceof Error ? error.message : String(error) }, 500);
|
|
@@ -32463,7 +32739,7 @@ var SkillRoutes = class extends Route {
|
|
|
32463
32739
|
// src/routes/tenant-credentials.ts
|
|
32464
32740
|
import { setCredentialStoreProvider } from "@mastra/code-sdk/agents/credential-resolver";
|
|
32465
32741
|
import { getOAuthProvider } from "@mastra/code-sdk/auth/storage";
|
|
32466
|
-
var
|
|
32742
|
+
var SNAPSHOT_TTL_MS2 = 15e3;
|
|
32467
32743
|
var MAX_CACHED_TENANTS = 1e3;
|
|
32468
32744
|
var TenantCredentialStore = class {
|
|
32469
32745
|
allowEnvironmentFallback = false;
|
|
@@ -32480,7 +32756,7 @@ var TenantCredentialStore = class {
|
|
|
32480
32756
|
}
|
|
32481
32757
|
/** Hydrate the snapshot when stale; coalesces concurrent callers. */
|
|
32482
32758
|
async ensureFresh(now = Date.now()) {
|
|
32483
|
-
if (now - this.#fetchedAt <
|
|
32759
|
+
if (now - this.#fetchedAt < SNAPSHOT_TTL_MS2) return;
|
|
32484
32760
|
this.#hydrating ??= this.#hydrate().finally(() => {
|
|
32485
32761
|
this.#hydrating = void 0;
|
|
32486
32762
|
});
|
|
@@ -32502,7 +32778,7 @@ var TenantCredentialStore = class {
|
|
|
32502
32778
|
}
|
|
32503
32779
|
/** Sync by contract; kicks a background re-hydrate when the snapshot is stale. */
|
|
32504
32780
|
reload() {
|
|
32505
|
-
if (Date.now() - this.#fetchedAt >=
|
|
32781
|
+
if (Date.now() - this.#fetchedAt >= SNAPSHOT_TTL_MS2) {
|
|
32506
32782
|
void this.ensureFresh().catch(() => {
|
|
32507
32783
|
});
|
|
32508
32784
|
}
|
|
@@ -34886,7 +35162,10 @@ function assembleFactoryApiRoutes(deps) {
|
|
|
34886
35162
|
authStorage: deps.authStorage,
|
|
34887
35163
|
modelCredentials: deps.domains.modelCredentials,
|
|
34888
35164
|
modelPacks: deps.domains.modelPacks,
|
|
34889
|
-
|
|
35165
|
+
memorySettings: deps.domains.memorySettings,
|
|
35166
|
+
customProviders: deps.domains.customProviders,
|
|
35167
|
+
onCredentialsChanged: invalidateTenantCredentialSnapshots,
|
|
35168
|
+
onCustomProvidersChanged: invalidateCustomProvidersSnapshots
|
|
34890
35169
|
}).routes(),
|
|
34891
35170
|
...new OAuthRoutes({
|
|
34892
35171
|
auth: deps.auth,
|
|
@@ -36051,10 +36330,13 @@ var MIME = {
|
|
|
36051
36330
|
};
|
|
36052
36331
|
var SERVER_PREFIXES = ["/api", "/web", "/auth"];
|
|
36053
36332
|
function resolveUiDistDir() {
|
|
36333
|
+
const projectRoot = process.env.MASTRA_PROJECT_ROOT?.trim();
|
|
36054
36334
|
const candidates = [
|
|
36055
36335
|
process.env.MASTRACODE_UI_DIST,
|
|
36056
|
-
|
|
36057
|
-
|
|
36336
|
+
resolve2(process.cwd(), "factory"),
|
|
36337
|
+
join2(dirname(fileURLToPath(import.meta.url)), "factory"),
|
|
36338
|
+
projectRoot && resolve2(projectRoot, "src/mastra/public/factory"),
|
|
36339
|
+
resolve2(process.cwd(), "src/mastra/public/factory")
|
|
36058
36340
|
];
|
|
36059
36341
|
for (const candidate of candidates) {
|
|
36060
36342
|
if (candidate && existsSync(join2(candidate, "index.html"))) return resolve2(candidate);
|
|
@@ -36480,8 +36762,154 @@ var AuditDomain = class {
|
|
|
36480
36762
|
}
|
|
36481
36763
|
};
|
|
36482
36764
|
|
|
36483
|
-
// src/storage/domains/
|
|
36765
|
+
// src/storage/domains/custom-providers/base.ts
|
|
36484
36766
|
import { FactoryStorageDomain as FactoryStorageDomain5, UniqueViolationError as UniqueViolationError5 } from "@mastra/core/storage";
|
|
36767
|
+
var CUSTOM_PROVIDERS_SCHEMA = {
|
|
36768
|
+
name: "custom_providers",
|
|
36769
|
+
columns: {
|
|
36770
|
+
id: { type: "uuid-pk" },
|
|
36771
|
+
org_id: { type: "text" },
|
|
36772
|
+
created_by: { type: "text" },
|
|
36773
|
+
provider_id: { type: "text" },
|
|
36774
|
+
name: { type: "text" },
|
|
36775
|
+
url: { type: "text" },
|
|
36776
|
+
api_key: { type: "text", nullable: true },
|
|
36777
|
+
models: { type: "json" },
|
|
36778
|
+
created_at: { type: "timestamp" },
|
|
36779
|
+
updated_at: { type: "timestamp" }
|
|
36780
|
+
},
|
|
36781
|
+
uniqueIndexes: [{ name: "custom_providers_org_provider_key", columns: ["org_id", "provider_id"] }]
|
|
36782
|
+
};
|
|
36783
|
+
function toRecord(row) {
|
|
36784
|
+
return {
|
|
36785
|
+
id: row.id,
|
|
36786
|
+
orgId: row.org_id,
|
|
36787
|
+
createdBy: row.created_by,
|
|
36788
|
+
providerId: row.provider_id,
|
|
36789
|
+
name: row.name,
|
|
36790
|
+
url: row.url,
|
|
36791
|
+
apiKey: row.api_key,
|
|
36792
|
+
models: Array.isArray(row.models) ? row.models : [],
|
|
36793
|
+
createdAt: row.created_at,
|
|
36794
|
+
updatedAt: row.updated_at
|
|
36795
|
+
};
|
|
36796
|
+
}
|
|
36797
|
+
var CustomProvidersStorage = class extends FactoryStorageDomain5 {
|
|
36798
|
+
constructor() {
|
|
36799
|
+
super("custom-providers");
|
|
36800
|
+
}
|
|
36801
|
+
async init() {
|
|
36802
|
+
await this.ensureCollections([CUSTOM_PROVIDERS_SCHEMA]);
|
|
36803
|
+
}
|
|
36804
|
+
async dangerouslyClearAll() {
|
|
36805
|
+
await this.ops.deleteMany("custom_providers", {});
|
|
36806
|
+
}
|
|
36807
|
+
get #db() {
|
|
36808
|
+
return this.ops;
|
|
36809
|
+
}
|
|
36810
|
+
/**
|
|
36811
|
+
* Create or wholesale-replace a provider by `(orgId, providerId)` — mirrors
|
|
36812
|
+
* the settings.json upsert semantics (no key retention: an absent `apiKey`
|
|
36813
|
+
* clears the stored key). `previousProviderId` renames the provider: the
|
|
36814
|
+
* common path is a single atomic in-place update of the old row; renaming
|
|
36815
|
+
* onto an id that already exists overwrites the target first and removes the
|
|
36816
|
+
* old row last, so no interleaving or failure can lose the provider.
|
|
36817
|
+
*/
|
|
36818
|
+
async upsert({
|
|
36819
|
+
orgId,
|
|
36820
|
+
userId,
|
|
36821
|
+
input,
|
|
36822
|
+
previousProviderId
|
|
36823
|
+
}) {
|
|
36824
|
+
const now = /* @__PURE__ */ new Date();
|
|
36825
|
+
const renameFrom = previousProviderId && previousProviderId !== input.providerId ? previousProviderId : void 0;
|
|
36826
|
+
if (renameFrom) {
|
|
36827
|
+
const target = await this.#db.findOne("custom_providers", {
|
|
36828
|
+
org_id: orgId,
|
|
36829
|
+
provider_id: input.providerId
|
|
36830
|
+
});
|
|
36831
|
+
if (!target) {
|
|
36832
|
+
const renamed = await this.#db.updateAtomic(
|
|
36833
|
+
"custom_providers",
|
|
36834
|
+
{ org_id: orgId, provider_id: renameFrom },
|
|
36835
|
+
() => ({
|
|
36836
|
+
provider_id: input.providerId,
|
|
36837
|
+
name: input.name,
|
|
36838
|
+
url: input.url,
|
|
36839
|
+
api_key: input.apiKey ?? null,
|
|
36840
|
+
models: input.models,
|
|
36841
|
+
updated_at: now
|
|
36842
|
+
})
|
|
36843
|
+
);
|
|
36844
|
+
if (renamed) return toRecord(renamed);
|
|
36845
|
+
}
|
|
36846
|
+
}
|
|
36847
|
+
const record = await this.#write({ orgId, userId, input, now });
|
|
36848
|
+
if (renameFrom) {
|
|
36849
|
+
await this.#db.deleteMany("custom_providers", { org_id: orgId, provider_id: renameFrom });
|
|
36850
|
+
}
|
|
36851
|
+
return record;
|
|
36852
|
+
}
|
|
36853
|
+
/**
|
|
36854
|
+
* Update-first, then insert-and-catch-unique-violation (see `queue_health`):
|
|
36855
|
+
* concurrent creates of the same provider both succeed (last write wins on
|
|
36856
|
+
* the single row) instead of one failing on the unique index, and a row
|
|
36857
|
+
* deleted mid-flight is recreated rather than reported as a stale success.
|
|
36858
|
+
*/
|
|
36859
|
+
async #write({
|
|
36860
|
+
orgId,
|
|
36861
|
+
userId,
|
|
36862
|
+
input,
|
|
36863
|
+
now
|
|
36864
|
+
}) {
|
|
36865
|
+
const updateExisting = () => this.#db.updateAtomic(
|
|
36866
|
+
"custom_providers",
|
|
36867
|
+
{ org_id: orgId, provider_id: input.providerId },
|
|
36868
|
+
() => ({
|
|
36869
|
+
name: input.name,
|
|
36870
|
+
url: input.url,
|
|
36871
|
+
api_key: input.apiKey ?? null,
|
|
36872
|
+
models: input.models,
|
|
36873
|
+
updated_at: now
|
|
36874
|
+
})
|
|
36875
|
+
);
|
|
36876
|
+
const updated = await updateExisting();
|
|
36877
|
+
if (updated) return toRecord(updated);
|
|
36878
|
+
try {
|
|
36879
|
+
const row = await this.#db.insertOne("custom_providers", {
|
|
36880
|
+
org_id: orgId,
|
|
36881
|
+
created_by: userId,
|
|
36882
|
+
provider_id: input.providerId,
|
|
36883
|
+
name: input.name,
|
|
36884
|
+
url: input.url,
|
|
36885
|
+
api_key: input.apiKey ?? null,
|
|
36886
|
+
models: input.models,
|
|
36887
|
+
created_at: now,
|
|
36888
|
+
updated_at: now
|
|
36889
|
+
});
|
|
36890
|
+
return toRecord(row);
|
|
36891
|
+
} catch (error) {
|
|
36892
|
+
if (!(error instanceof UniqueViolationError5)) throw error;
|
|
36893
|
+
const row = await updateExisting();
|
|
36894
|
+
if (!row) throw error;
|
|
36895
|
+
return toRecord(row);
|
|
36896
|
+
}
|
|
36897
|
+
}
|
|
36898
|
+
async list({ orgId }) {
|
|
36899
|
+
const rows = await this.#db.findMany(
|
|
36900
|
+
"custom_providers",
|
|
36901
|
+
{ org_id: orgId },
|
|
36902
|
+
{ orderBy: [["name", "asc"]] }
|
|
36903
|
+
);
|
|
36904
|
+
return rows.map(toRecord);
|
|
36905
|
+
}
|
|
36906
|
+
async delete({ orgId, providerId }) {
|
|
36907
|
+
return await this.#db.deleteMany("custom_providers", { org_id: orgId, provider_id: providerId }) > 0;
|
|
36908
|
+
}
|
|
36909
|
+
};
|
|
36910
|
+
|
|
36911
|
+
// src/storage/domains/intake/base.ts
|
|
36912
|
+
import { FactoryStorageDomain as FactoryStorageDomain6, UniqueViolationError as UniqueViolationError6 } from "@mastra/core/storage";
|
|
36485
36913
|
var DEFAULT_INTAKE_CONFIG = {};
|
|
36486
36914
|
var INTAKE_SETTINGS_SCHEMA = {
|
|
36487
36915
|
name: "intake_settings",
|
|
@@ -36495,7 +36923,7 @@ var INTAKE_SETTINGS_SCHEMA = {
|
|
|
36495
36923
|
},
|
|
36496
36924
|
uniqueIndexes: [{ name: "intake_settings_org_user_unique", columns: ["org_id", "user_id"] }]
|
|
36497
36925
|
};
|
|
36498
|
-
var IntakeStorage = class extends
|
|
36926
|
+
var IntakeStorage = class extends FactoryStorageDomain6 {
|
|
36499
36927
|
constructor() {
|
|
36500
36928
|
super("intake");
|
|
36501
36929
|
}
|
|
@@ -36531,14 +36959,14 @@ var IntakeStorage = class extends FactoryStorageDomain5 {
|
|
|
36531
36959
|
try {
|
|
36532
36960
|
await this.#db.insertOne("intake_settings", { ...where, config, created_at: now, updated_at: now });
|
|
36533
36961
|
} catch (error) {
|
|
36534
|
-
if (!(error instanceof
|
|
36962
|
+
if (!(error instanceof UniqueViolationError6)) throw error;
|
|
36535
36963
|
await this.#db.updateMany("intake_settings", where, { config, updated_at: now });
|
|
36536
36964
|
}
|
|
36537
36965
|
}
|
|
36538
36966
|
};
|
|
36539
36967
|
|
|
36540
36968
|
// src/storage/domains/integrations/base.ts
|
|
36541
|
-
import { FactoryStorageDomain as
|
|
36969
|
+
import { FactoryStorageDomain as FactoryStorageDomain7, UniqueViolationError as UniqueViolationError7 } from "@mastra/core/storage";
|
|
36542
36970
|
var INTEGRATION_CONNECTIONS_SCHEMA = {
|
|
36543
36971
|
name: "integration_connections",
|
|
36544
36972
|
columns: {
|
|
@@ -36591,7 +37019,7 @@ var INTEGRATION_SETTINGS_SCHEMA = {
|
|
|
36591
37019
|
{ name: "integration_settings_integration_org_user_unique", columns: ["integration_id", "org_id", "user_id"] }
|
|
36592
37020
|
]
|
|
36593
37021
|
};
|
|
36594
|
-
var IntegrationStorage = class extends
|
|
37022
|
+
var IntegrationStorage = class extends FactoryStorageDomain7 {
|
|
36595
37023
|
constructor() {
|
|
36596
37024
|
super("integrations");
|
|
36597
37025
|
}
|
|
@@ -36667,7 +37095,7 @@ var IntegrationStorage = class extends FactoryStorageDomain6 {
|
|
|
36667
37095
|
});
|
|
36668
37096
|
return;
|
|
36669
37097
|
} catch (error) {
|
|
36670
|
-
if (!(error instanceof
|
|
37098
|
+
if (!(error instanceof UniqueViolationError7)) throw error;
|
|
36671
37099
|
lastError = error;
|
|
36672
37100
|
}
|
|
36673
37101
|
}
|
|
@@ -36772,7 +37200,7 @@ var IntegrationStorage = class extends FactoryStorageDomain6 {
|
|
|
36772
37200
|
});
|
|
36773
37201
|
return;
|
|
36774
37202
|
} catch (error) {
|
|
36775
|
-
if (!(error instanceof
|
|
37203
|
+
if (!(error instanceof UniqueViolationError7)) throw error;
|
|
36776
37204
|
lastError = error;
|
|
36777
37205
|
}
|
|
36778
37206
|
}
|
|
@@ -36783,8 +37211,115 @@ var IntegrationStorage = class extends FactoryStorageDomain6 {
|
|
|
36783
37211
|
}
|
|
36784
37212
|
};
|
|
36785
37213
|
|
|
37214
|
+
// src/storage/domains/memory-settings/base.ts
|
|
37215
|
+
import { FactoryStorageDomain as FactoryStorageDomain8, UniqueViolationError as UniqueViolationError8 } from "@mastra/core/storage";
|
|
37216
|
+
var MEMORY_SETTINGS_SCHEMA = {
|
|
37217
|
+
name: "memory_settings",
|
|
37218
|
+
columns: {
|
|
37219
|
+
id: { type: "uuid-pk" },
|
|
37220
|
+
org_id: { type: "text" },
|
|
37221
|
+
user_id: { type: "text" },
|
|
37222
|
+
observer_model_id: { type: "text", nullable: true },
|
|
37223
|
+
reflector_model_id: { type: "text", nullable: true },
|
|
37224
|
+
observation_threshold: { type: "integer", nullable: true },
|
|
37225
|
+
reflection_threshold: { type: "integer", nullable: true },
|
|
37226
|
+
// 'auto' | true | false — json keeps the tri-state without string encoding.
|
|
37227
|
+
observe_attachments: { type: "json", nullable: true },
|
|
37228
|
+
created_at: { type: "timestamp" },
|
|
37229
|
+
updated_at: { type: "timestamp" }
|
|
37230
|
+
},
|
|
37231
|
+
uniqueIndexes: [{ name: "memory_settings_org_user_key", columns: ["org_id", "user_id"] }]
|
|
37232
|
+
};
|
|
37233
|
+
function toRecord2(row) {
|
|
37234
|
+
return {
|
|
37235
|
+
orgId: row.org_id,
|
|
37236
|
+
userId: row.user_id,
|
|
37237
|
+
observerModelId: row.observer_model_id,
|
|
37238
|
+
reflectorModelId: row.reflector_model_id,
|
|
37239
|
+
observationThreshold: row.observation_threshold,
|
|
37240
|
+
reflectionThreshold: row.reflection_threshold,
|
|
37241
|
+
observeAttachments: row.observe_attachments,
|
|
37242
|
+
createdAt: row.created_at,
|
|
37243
|
+
updatedAt: row.updated_at
|
|
37244
|
+
};
|
|
37245
|
+
}
|
|
37246
|
+
function patchToColumns(patch) {
|
|
37247
|
+
const columns = {};
|
|
37248
|
+
if (patch.observerModelId !== void 0) columns.observer_model_id = patch.observerModelId;
|
|
37249
|
+
if (patch.reflectorModelId !== void 0) columns.reflector_model_id = patch.reflectorModelId;
|
|
37250
|
+
if (patch.observationThreshold !== void 0) columns.observation_threshold = patch.observationThreshold;
|
|
37251
|
+
if (patch.reflectionThreshold !== void 0) columns.reflection_threshold = patch.reflectionThreshold;
|
|
37252
|
+
if (patch.observeAttachments !== void 0) columns.observe_attachments = patch.observeAttachments;
|
|
37253
|
+
return columns;
|
|
37254
|
+
}
|
|
37255
|
+
var MemorySettingsStorage = class extends FactoryStorageDomain8 {
|
|
37256
|
+
constructor() {
|
|
37257
|
+
super("memory-settings");
|
|
37258
|
+
}
|
|
37259
|
+
async init() {
|
|
37260
|
+
await this.ensureCollections([MEMORY_SETTINGS_SCHEMA]);
|
|
37261
|
+
}
|
|
37262
|
+
async dangerouslyClearAll() {
|
|
37263
|
+
await this.ops.deleteMany("memory_settings", {});
|
|
37264
|
+
}
|
|
37265
|
+
get #db() {
|
|
37266
|
+
return this.ops;
|
|
37267
|
+
}
|
|
37268
|
+
async get({ orgId, userId }) {
|
|
37269
|
+
const row = await this.#db.findOne("memory_settings", { org_id: orgId, user_id: userId });
|
|
37270
|
+
return row ? toRecord2(row) : null;
|
|
37271
|
+
}
|
|
37272
|
+
/**
|
|
37273
|
+
* Upsert the user's row, writing only the knobs present in `patch`.
|
|
37274
|
+
* `fillIfUnset` knobs are written only where the stored value is still
|
|
37275
|
+
* `NULL`, decided inside the atomic update so concurrent explicit writes
|
|
37276
|
+
* win over fills. Concurrent first writes are resolved via the shared
|
|
37277
|
+
* insert-then-catch-unique-violation pattern (see `queue_health`).
|
|
37278
|
+
*/
|
|
37279
|
+
async patch({
|
|
37280
|
+
orgId,
|
|
37281
|
+
userId,
|
|
37282
|
+
patch,
|
|
37283
|
+
fillIfUnset
|
|
37284
|
+
}) {
|
|
37285
|
+
const now = /* @__PURE__ */ new Date();
|
|
37286
|
+
const updateExisting = () => this.#db.updateAtomic("memory_settings", { org_id: orgId, user_id: userId }, (row) => {
|
|
37287
|
+
const columns = { ...patchToColumns(patch), updated_at: now };
|
|
37288
|
+
if (fillIfUnset?.observerModelId !== void 0 && row.observer_model_id == null) {
|
|
37289
|
+
columns.observer_model_id = patch.observerModelId ?? fillIfUnset.observerModelId;
|
|
37290
|
+
}
|
|
37291
|
+
if (fillIfUnset?.reflectorModelId !== void 0 && row.reflector_model_id == null) {
|
|
37292
|
+
columns.reflector_model_id = patch.reflectorModelId ?? fillIfUnset.reflectorModelId;
|
|
37293
|
+
}
|
|
37294
|
+
return columns;
|
|
37295
|
+
});
|
|
37296
|
+
const updated = await updateExisting();
|
|
37297
|
+
if (updated) return toRecord2(updated);
|
|
37298
|
+
try {
|
|
37299
|
+
const row = await this.#db.insertOne("memory_settings", {
|
|
37300
|
+
org_id: orgId,
|
|
37301
|
+
user_id: userId,
|
|
37302
|
+
observer_model_id: fillIfUnset?.observerModelId ?? null,
|
|
37303
|
+
reflector_model_id: fillIfUnset?.reflectorModelId ?? null,
|
|
37304
|
+
observation_threshold: null,
|
|
37305
|
+
reflection_threshold: null,
|
|
37306
|
+
observe_attachments: null,
|
|
37307
|
+
...patchToColumns(patch),
|
|
37308
|
+
created_at: now,
|
|
37309
|
+
updated_at: now
|
|
37310
|
+
});
|
|
37311
|
+
return toRecord2(row);
|
|
37312
|
+
} catch (error) {
|
|
37313
|
+
if (!(error instanceof UniqueViolationError8)) throw error;
|
|
37314
|
+
const row = await updateExisting();
|
|
37315
|
+
if (!row) throw error;
|
|
37316
|
+
return toRecord2(row);
|
|
37317
|
+
}
|
|
37318
|
+
}
|
|
37319
|
+
};
|
|
37320
|
+
|
|
36786
37321
|
// src/storage/domains/model-packs/base.ts
|
|
36787
|
-
import { FactoryStorageDomain as
|
|
37322
|
+
import { FactoryStorageDomain as FactoryStorageDomain9 } from "@mastra/core/storage";
|
|
36788
37323
|
var MODEL_PACKS_SCHEMA = {
|
|
36789
37324
|
name: "model_packs",
|
|
36790
37325
|
columns: {
|
|
@@ -36811,7 +37346,7 @@ function toModelPack(row) {
|
|
|
36811
37346
|
updatedAt: row.updated_at
|
|
36812
37347
|
};
|
|
36813
37348
|
}
|
|
36814
|
-
var ModelPacksStorage = class extends
|
|
37349
|
+
var ModelPacksStorage = class extends FactoryStorageDomain9 {
|
|
36815
37350
|
constructor() {
|
|
36816
37351
|
super("model-packs");
|
|
36817
37352
|
}
|
|
@@ -36875,7 +37410,7 @@ var ModelPacksStorage = class extends FactoryStorageDomain7 {
|
|
|
36875
37410
|
};
|
|
36876
37411
|
|
|
36877
37412
|
// src/storage/domains/projects/base.ts
|
|
36878
|
-
import { FactoryStorageDomain as
|
|
37413
|
+
import { FactoryStorageDomain as FactoryStorageDomain10 } from "@mastra/core/storage";
|
|
36879
37414
|
var FACTORY_PROJECTS_SCHEMA = {
|
|
36880
37415
|
name: "factory_projects",
|
|
36881
37416
|
columns: {
|
|
@@ -36902,7 +37437,7 @@ function toFactoryProject(row) {
|
|
|
36902
37437
|
updatedAt: row.updated_at
|
|
36903
37438
|
};
|
|
36904
37439
|
}
|
|
36905
|
-
var FactoryProjectsStorage = class extends
|
|
37440
|
+
var FactoryProjectsStorage = class extends FactoryStorageDomain10 {
|
|
36906
37441
|
constructor() {
|
|
36907
37442
|
super("projects");
|
|
36908
37443
|
}
|
|
@@ -36970,7 +37505,7 @@ var FactoryProjectsStorage = class extends FactoryStorageDomain8 {
|
|
|
36970
37505
|
};
|
|
36971
37506
|
|
|
36972
37507
|
// src/storage/domains/source-control/base.ts
|
|
36973
|
-
import { FactoryStorageDomain as
|
|
37508
|
+
import { FactoryStorageDomain as FactoryStorageDomain11, UniqueViolationError as UniqueViolationError9 } from "@mastra/core/storage";
|
|
36974
37509
|
var FACTORY_PROJECTS = "factory_projects";
|
|
36975
37510
|
var INSTALLATIONS = "source_control_installations";
|
|
36976
37511
|
var REPOSITORIES = "source_control_repositories";
|
|
@@ -37223,7 +37758,7 @@ function toSession(row) {
|
|
|
37223
37758
|
updatedAt: row.updated_at
|
|
37224
37759
|
};
|
|
37225
37760
|
}
|
|
37226
|
-
var SourceControlStorage = class extends
|
|
37761
|
+
var SourceControlStorage = class extends FactoryStorageDomain11 {
|
|
37227
37762
|
constructor() {
|
|
37228
37763
|
super("source-control");
|
|
37229
37764
|
}
|
|
@@ -37417,7 +37952,7 @@ var SourceControlStorage = class extends FactoryStorageDomain9 {
|
|
|
37417
37952
|
});
|
|
37418
37953
|
return toConnection(row);
|
|
37419
37954
|
} catch (error) {
|
|
37420
|
-
if (!(error instanceof
|
|
37955
|
+
if (!(error instanceof UniqueViolationError9)) throw error;
|
|
37421
37956
|
const row = await db().findOne(CONNECTIONS, {
|
|
37422
37957
|
factory_project_id: input.factoryProjectId,
|
|
37423
37958
|
integration_id: integrationId,
|
|
@@ -37546,7 +38081,7 @@ var SourceControlStorage = class extends FactoryStorageDomain9 {
|
|
|
37546
38081
|
});
|
|
37547
38082
|
return toSandbox(row);
|
|
37548
38083
|
} catch (error) {
|
|
37549
|
-
if (!(error instanceof
|
|
38084
|
+
if (!(error instanceof UniqueViolationError9)) throw error;
|
|
37550
38085
|
const row = await db().findOne(SANDBOXES, where);
|
|
37551
38086
|
if (!row) throw error;
|
|
37552
38087
|
return toSandbox(row);
|
|
@@ -37659,7 +38194,7 @@ var SourceControlStorage = class extends FactoryStorageDomain9 {
|
|
|
37659
38194
|
});
|
|
37660
38195
|
return toSession(row);
|
|
37661
38196
|
} catch (error) {
|
|
37662
|
-
if (!(error instanceof
|
|
38197
|
+
if (!(error instanceof UniqueViolationError9)) throw error;
|
|
37663
38198
|
const row = await db().findOne(SESSIONS, {
|
|
37664
38199
|
project_repository_id: input.projectRepositoryId,
|
|
37665
38200
|
user_id: input.userId,
|
|
@@ -37762,6 +38297,7 @@ var factorySkillExtension = {
|
|
|
37762
38297
|
function createWorkspaceFactory(options = {}) {
|
|
37763
38298
|
const { sandbox: sandboxConfig, github, fleet } = options;
|
|
37764
38299
|
const isLocalSandbox = sandboxConfig?.machine instanceof LocalSandbox;
|
|
38300
|
+
const githubTokenInjectors = /* @__PURE__ */ new Map();
|
|
37765
38301
|
return async ({ requestContext, mastra, skillExtension }) => {
|
|
37766
38302
|
const effectiveSkillExtension = skillExtension ?? factorySkillExtension;
|
|
37767
38303
|
const ctx = requestContext.get("controller");
|
|
@@ -37813,6 +38349,8 @@ function createWorkspaceFactory(options = {}) {
|
|
|
37813
38349
|
const existing = mastra?.getWorkspaceById(workspaceId);
|
|
37814
38350
|
if (existing) {
|
|
37815
38351
|
existing.setToolsConfig(MASTRACODE_WORKSPACE_TOOLS);
|
|
38352
|
+
const injectGithubToken3 = githubTokenInjectors.get(workspaceId);
|
|
38353
|
+
if (injectGithubToken3) registerGithubTokenInjector(requestContext, injectGithubToken3);
|
|
37816
38354
|
return existing;
|
|
37817
38355
|
}
|
|
37818
38356
|
} catch {
|
|
@@ -37843,6 +38381,14 @@ function createWorkspaceFactory(options = {}) {
|
|
|
37843
38381
|
repoFullName
|
|
37844
38382
|
});
|
|
37845
38383
|
if (projectRepository.setupCommand) await runWorktreeSetup(sandbox, workdir, projectRepository.setupCommand);
|
|
38384
|
+
const injectGithubToken2 = (freshToken) => {
|
|
38385
|
+
if (!sandbox.setEnvironmentVariable) {
|
|
38386
|
+
throw new Error("The active sandbox provider does not support runtime GitHub token refresh.");
|
|
38387
|
+
}
|
|
38388
|
+
sandbox.setEnvironmentVariable("GH_TOKEN", freshToken);
|
|
38389
|
+
};
|
|
38390
|
+
githubTokenInjectors.set(workspaceId, injectGithubToken2);
|
|
38391
|
+
registerGithubTokenInjector(requestContext, injectGithubToken2);
|
|
37846
38392
|
const filesystem = new SandboxFilesystem({ sandbox, workdir });
|
|
37847
38393
|
const projectSkillPaths = [path2.join(configDir, "skills"), ".claude/skills", ".agents/skills"];
|
|
37848
38394
|
const skillPaths = [...effectiveSkillExtension?.paths ?? [], ...projectSkillPaths];
|
|
@@ -37950,6 +38496,8 @@ var MastraFactory = class {
|
|
|
37950
38496
|
const workItemsStorage = storage.registerDomain(new WorkItemsStorage());
|
|
37951
38497
|
const modelCredentialsStorage = storage.registerDomain(new ModelCredentialsStorage());
|
|
37952
38498
|
const modelPacksStorage = storage.registerDomain(new ModelPacksStorage());
|
|
38499
|
+
const memorySettingsStorage = storage.registerDomain(new MemorySettingsStorage());
|
|
38500
|
+
const customProvidersStorage = storage.registerDomain(new CustomProvidersStorage());
|
|
37953
38501
|
const queueHealthStorage = storage.registerDomain(new QueueHealthStorage());
|
|
37954
38502
|
const integrationStorage = storage.registerDomain(new IntegrationStorage());
|
|
37955
38503
|
const factoryProjectsStorage = storage.registerDomain(new FactoryProjectsStorage());
|
|
@@ -37958,6 +38506,8 @@ var MastraFactory = class {
|
|
|
37958
38506
|
intake: intakeStorage,
|
|
37959
38507
|
modelCredentials: modelCredentialsStorage,
|
|
37960
38508
|
modelPacks: modelPacksStorage,
|
|
38509
|
+
memorySettings: memorySettingsStorage,
|
|
38510
|
+
customProviders: customProvidersStorage,
|
|
37961
38511
|
projects: factoryProjectsStorage,
|
|
37962
38512
|
queueHealth: queueHealthStorage,
|
|
37963
38513
|
workItems: workItemsStorage
|
|
@@ -38006,6 +38556,7 @@ var MastraFactory = class {
|
|
|
38006
38556
|
if (auth) {
|
|
38007
38557
|
registerTenantCredentialResolver(modelCredentialsStorage);
|
|
38008
38558
|
}
|
|
38559
|
+
registerCustomProvidersSource({ storage: customProvidersStorage, authEnabled: Boolean(auth) });
|
|
38009
38560
|
for (const integration of integrations) {
|
|
38010
38561
|
integration.initialize?.({
|
|
38011
38562
|
storage: integrationStorage.forIntegration(integration.id),
|
|
@@ -38070,6 +38621,9 @@ var MastraFactory = class {
|
|
|
38070
38621
|
fleet
|
|
38071
38622
|
}),
|
|
38072
38623
|
disableGithubSignals: true,
|
|
38624
|
+
// Memory settings live in the factory's `memory-settings` app table (per
|
|
38625
|
+
// org/user), so the host machine's TUI settings.json must not seed them.
|
|
38626
|
+
disableSettingsOmSeed: true,
|
|
38073
38627
|
storage: storage.getMastraStorage(),
|
|
38074
38628
|
...factoryProcessor ? { inputProcessors: [factoryProcessor] } : {},
|
|
38075
38629
|
...vector ? { vector } : {},
|
|
@@ -38178,13 +38732,25 @@ var MastraFactory = class {
|
|
|
38178
38732
|
const uiDist = resolveUiDistDir();
|
|
38179
38733
|
const spa = uiDist ? [createSpaStaticMiddleware(uiDist)] : [];
|
|
38180
38734
|
if (!auth) {
|
|
38181
|
-
return {
|
|
38735
|
+
return {
|
|
38736
|
+
middleware: [
|
|
38737
|
+
createCustomProvidersPrimer({ auth: routeAuth, storage: customProvidersStorage, authEnabled: false }),
|
|
38738
|
+
...spa
|
|
38739
|
+
],
|
|
38740
|
+
...cors,
|
|
38741
|
+
...onError
|
|
38742
|
+
};
|
|
38182
38743
|
}
|
|
38183
38744
|
return {
|
|
38184
38745
|
auth,
|
|
38185
38746
|
middleware: [
|
|
38186
38747
|
createFactoryAuthGate(auth),
|
|
38187
38748
|
createTenantCredentialPrimer({ auth: routeAuth, credentials: modelCredentialsStorage }),
|
|
38749
|
+
createCustomProvidersPrimer({
|
|
38750
|
+
auth: routeAuth,
|
|
38751
|
+
storage: customProvidersStorage,
|
|
38752
|
+
authEnabled: Boolean(auth)
|
|
38753
|
+
}),
|
|
38188
38754
|
...spa
|
|
38189
38755
|
],
|
|
38190
38756
|
...cors,
|