@agent-native/dispatch 0.0.0-beta-20260819184657 → 0.0.0-beta-20260820150521
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/server/lib/app-creation-store.d.ts.map +1 -1
- package/dist/server/lib/app-creation-store.js +178 -14
- package/dist/server/lib/app-creation-store.js.map +1 -1
- package/package.json +4 -4
- package/src/server/lib/app-creation-store.spec.ts +296 -19
- package/src/server/lib/app-creation-store.ts +218 -14
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"app-creation-store.d.ts","sourceRoot":"","sources":["../../../src/server/lib/app-creation-store.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"app-creation-store.d.ts","sourceRoot":"","sources":["../../../src/server/lib/app-creation-store.ts"],"names":[],"mappings":"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"}
|
|
@@ -7,7 +7,7 @@ import { getDbExec } from "@agent-native/core/db";
|
|
|
7
7
|
import { getOrgA2ASecret, getOrgDomain } from "@agent-native/core/org";
|
|
8
8
|
import { ensureBuilderProject, getBuilderBranchProjectId, getRequestContext, isIntegrationCallerRequest, resolveBuilderCredentialsDetailed, runBuilderAgent, } from "@agent-native/core/server";
|
|
9
9
|
import { getOrgSetting } from "@agent-native/core/settings";
|
|
10
|
-
import { getSetting, putSetting } from "@agent-native/core/settings";
|
|
10
|
+
import { getSetting, mutateSetting, putSetting, } from "@agent-native/core/settings";
|
|
11
11
|
import { assertValidWorkspaceAppId } from "@agent-native/core/shared";
|
|
12
12
|
import { resolveAccess } from "@agent-native/core/sharing";
|
|
13
13
|
// Register the workspace-app shareable resource before any access lookup.
|
|
@@ -552,6 +552,9 @@ function parsePendingWorkspaceApps(value) {
|
|
|
552
552
|
...(record.audience === undefined
|
|
553
553
|
? {}
|
|
554
554
|
: { audience: normalizeWorkspaceAppAudience(record.audience) }),
|
|
555
|
+
...(record.visibility === "private" || record.visibility === "org"
|
|
556
|
+
? { visibility: record.visibility }
|
|
557
|
+
: {}),
|
|
555
558
|
createdAt,
|
|
556
559
|
updatedAt: typeof record.updatedAt === "string" && record.updatedAt.trim()
|
|
557
560
|
? record.updatedAt.trim()
|
|
@@ -567,6 +570,97 @@ async function listPendingWorkspaceApps() {
|
|
|
567
570
|
const raw = await readSettingsRecord();
|
|
568
571
|
return parsePendingWorkspaceApps(raw.pendingApps);
|
|
569
572
|
}
|
|
573
|
+
async function assertPendingWorkspaceAppCreationAvailable(appId) {
|
|
574
|
+
const existing = (await listPendingWorkspaceApps())
|
|
575
|
+
.filter((app) => !isPendingWorkspaceAppExpired(app))
|
|
576
|
+
.find((app) => app.id === appId);
|
|
577
|
+
if (!existing)
|
|
578
|
+
return;
|
|
579
|
+
const viewerEmail = currentOwnerEmail().trim().toLowerCase();
|
|
580
|
+
const ownerEmail = (existing.createdBy ?? existing.owner)
|
|
581
|
+
?.trim()
|
|
582
|
+
.toLowerCase();
|
|
583
|
+
if (ownerEmail && ownerEmail !== viewerEmail) {
|
|
584
|
+
throw new Error(`Workspace app "${appId}" is already being created by another member.`);
|
|
585
|
+
}
|
|
586
|
+
}
|
|
587
|
+
async function assertWorkspaceAppIdRegisteredFree(appId) {
|
|
588
|
+
let existing;
|
|
589
|
+
try {
|
|
590
|
+
const result = await getDbExec().execute({
|
|
591
|
+
sql: "SELECT id FROM workspace_apps WHERE id = ? LIMIT 1",
|
|
592
|
+
args: [appId],
|
|
593
|
+
});
|
|
594
|
+
existing = result.rows;
|
|
595
|
+
}
|
|
596
|
+
catch {
|
|
597
|
+
throw new Error("Could not verify the workspace app registry; refusing to reuse an app id.");
|
|
598
|
+
}
|
|
599
|
+
if (existing.length > 0) {
|
|
600
|
+
throw new Error(`Workspace app "${appId}" is already registered.`);
|
|
601
|
+
}
|
|
602
|
+
}
|
|
603
|
+
async function reservePendingWorkspaceApp(input) {
|
|
604
|
+
await assertWorkspaceAppIdRegisteredFree(input.appId);
|
|
605
|
+
const now = new Date().toISOString();
|
|
606
|
+
const context = pendingWorkspaceAppContext();
|
|
607
|
+
const creatorEmail = currentOwnerEmail();
|
|
608
|
+
await mutateSetting(scopedSettingsKey(), async (current) => {
|
|
609
|
+
const raw = current && typeof current === "object" && !Array.isArray(current)
|
|
610
|
+
? current
|
|
611
|
+
: {};
|
|
612
|
+
const pendingApps = parsePendingWorkspaceApps(raw.pendingApps);
|
|
613
|
+
const existing = pendingApps
|
|
614
|
+
.filter((app) => !isPendingWorkspaceAppExpired(app))
|
|
615
|
+
.find((app) => app.id === input.appId);
|
|
616
|
+
if (existing) {
|
|
617
|
+
throw new Error(`Workspace app "${input.appId}" is already being created${existing.createdBy || existing.owner
|
|
618
|
+
? ` by ${existing.createdBy ?? existing.owner}`
|
|
619
|
+
: ""}.`);
|
|
620
|
+
}
|
|
621
|
+
const reservation = {
|
|
622
|
+
id: input.appId,
|
|
623
|
+
name: titleCase(input.appId),
|
|
624
|
+
description: input.description ||
|
|
625
|
+
"Builder is creating this app. The workspace path becomes live after the branch is merged and deployed.",
|
|
626
|
+
path: `/${input.appId}`,
|
|
627
|
+
builderUrl: null,
|
|
628
|
+
branchName: null,
|
|
629
|
+
projectId: input.projectId,
|
|
630
|
+
contextId: context?.id ?? null,
|
|
631
|
+
contextLabel: context?.label ?? null,
|
|
632
|
+
visibility: input.visibility,
|
|
633
|
+
createdBy: creatorEmail,
|
|
634
|
+
owner: creatorEmail,
|
|
635
|
+
createdAt: now,
|
|
636
|
+
updatedAt: now,
|
|
637
|
+
expiresAt: pendingWorkspaceAppExpiresAt(now),
|
|
638
|
+
};
|
|
639
|
+
return {
|
|
640
|
+
...raw,
|
|
641
|
+
pendingApps: [reservation, ...pendingApps].slice(0, MAX_PENDING_APPS),
|
|
642
|
+
};
|
|
643
|
+
});
|
|
644
|
+
}
|
|
645
|
+
async function releasePendingWorkspaceAppReservation(appId) {
|
|
646
|
+
const contextId = pendingWorkspaceAppContext()?.id ?? null;
|
|
647
|
+
const creatorEmail = currentOwnerEmail().trim().toLowerCase();
|
|
648
|
+
await mutateSetting(scopedSettingsKey(), async (current) => {
|
|
649
|
+
const raw = current && typeof current === "object" && !Array.isArray(current)
|
|
650
|
+
? current
|
|
651
|
+
: {};
|
|
652
|
+
const pendingApps = parsePendingWorkspaceApps(raw.pendingApps);
|
|
653
|
+
return {
|
|
654
|
+
...raw,
|
|
655
|
+
pendingApps: pendingApps.filter((app) => {
|
|
656
|
+
if (app.id !== appId || app.contextId !== contextId)
|
|
657
|
+
return true;
|
|
658
|
+
const owner = (app.createdBy ?? app.owner)?.trim().toLowerCase();
|
|
659
|
+
return owner !== creatorEmail;
|
|
660
|
+
}),
|
|
661
|
+
};
|
|
662
|
+
});
|
|
663
|
+
}
|
|
570
664
|
function parseArchivedAppIds(value) {
|
|
571
665
|
if (!Array.isArray(value))
|
|
572
666
|
return [];
|
|
@@ -643,6 +737,7 @@ function pendingAppToSummary(app) {
|
|
|
643
737
|
protectedPaths: [],
|
|
644
738
|
status: "pending",
|
|
645
739
|
statusLabel: "Pending Builder branch",
|
|
740
|
+
visibility: app.visibility,
|
|
646
741
|
builderUrl: app.builderUrl,
|
|
647
742
|
branchName: app.branchName,
|
|
648
743
|
createdAt: app.createdAt,
|
|
@@ -776,7 +871,6 @@ async function ensureWorkspaceAppRecords(apps) {
|
|
|
776
871
|
return apps;
|
|
777
872
|
const orgId = currentOrgId();
|
|
778
873
|
const metadata = await readWorkspaceAppMetadataSettings();
|
|
779
|
-
const defaultVisibility = await workspaceAppDefaultVisibility();
|
|
780
874
|
const db = getDbExec();
|
|
781
875
|
const records = new Map();
|
|
782
876
|
try {
|
|
@@ -806,13 +900,12 @@ async function ensureWorkspaceAppRecords(apps) {
|
|
|
806
900
|
const ownerEmail = cleanOptionalText(override?.createdBy) ??
|
|
807
901
|
cleanOptionalText(app.createdBy) ??
|
|
808
902
|
"";
|
|
809
|
-
const hasTrustedCreationMetadata = Boolean(ownerEmail);
|
|
810
903
|
const visibility = override?.visibility === "private"
|
|
811
904
|
? "private"
|
|
812
905
|
: override?.visibility === "org"
|
|
813
906
|
? "org"
|
|
814
|
-
:
|
|
815
|
-
?
|
|
907
|
+
: app.visibility === "private"
|
|
908
|
+
? "private"
|
|
816
909
|
: "org";
|
|
817
910
|
const createdAt = appRecordTimestamp(app.createdAt);
|
|
818
911
|
await db.execute({
|
|
@@ -870,7 +963,18 @@ async function filterWorkspaceAppsByAccess(apps) {
|
|
|
870
963
|
const visibleIds = new Set();
|
|
871
964
|
const candidates = [];
|
|
872
965
|
for (const app of apps) {
|
|
873
|
-
if (app.status === "pending"
|
|
966
|
+
if (app.status === "pending") {
|
|
967
|
+
const viewerEmail = userEmail.toLowerCase();
|
|
968
|
+
const creatorEmail = app.createdBy?.trim().toLowerCase();
|
|
969
|
+
const ownerEmail = app.owner?.trim().toLowerCase();
|
|
970
|
+
if (app.visibility !== "private" ||
|
|
971
|
+
creatorEmail === viewerEmail ||
|
|
972
|
+
ownerEmail === viewerEmail) {
|
|
973
|
+
visibleIds.add(app.id);
|
|
974
|
+
}
|
|
975
|
+
continue;
|
|
976
|
+
}
|
|
977
|
+
if (app.isDispatch) {
|
|
874
978
|
visibleIds.add(app.id);
|
|
875
979
|
continue;
|
|
876
980
|
}
|
|
@@ -910,6 +1014,12 @@ async function recordPendingWorkspaceApp(input) {
|
|
|
910
1014
|
const existing = pendingApps
|
|
911
1015
|
.filter((app) => !isPendingWorkspaceAppExpired(app))
|
|
912
1016
|
.find(samePendingEntry);
|
|
1017
|
+
const creatorEmail = currentOwnerEmail();
|
|
1018
|
+
const existingOwnerEmail = existing?.createdBy ?? existing?.owner;
|
|
1019
|
+
if (existingOwnerEmail &&
|
|
1020
|
+
existingOwnerEmail.trim().toLowerCase() !== creatorEmail.toLowerCase()) {
|
|
1021
|
+
throw new Error(`Workspace app "${input.appId}" is already being created by another member.`);
|
|
1022
|
+
}
|
|
913
1023
|
const next = {
|
|
914
1024
|
id: input.appId,
|
|
915
1025
|
name: titleCase(input.appId),
|
|
@@ -921,8 +1031,9 @@ async function recordPendingWorkspaceApp(input) {
|
|
|
921
1031
|
projectId: input.projectId,
|
|
922
1032
|
contextId: context?.id ?? null,
|
|
923
1033
|
contextLabel: context?.label ?? null,
|
|
924
|
-
|
|
925
|
-
|
|
1034
|
+
visibility: input.visibility,
|
|
1035
|
+
createdBy: existing?.createdBy ?? existing?.owner ?? creatorEmail,
|
|
1036
|
+
owner: existing?.owner ?? existing?.createdBy ?? creatorEmail,
|
|
926
1037
|
createdAt: existing?.createdAt || now,
|
|
927
1038
|
updatedAt: now,
|
|
928
1039
|
expiresAt: pendingWorkspaceAppExpiresAt(existing?.createdAt || now),
|
|
@@ -939,8 +1050,9 @@ async function recordPendingWorkspaceApp(input) {
|
|
|
939
1050
|
description: input.description,
|
|
940
1051
|
generated: true,
|
|
941
1052
|
sourcePrompt: input.sourcePrompt,
|
|
942
|
-
updatedBy:
|
|
943
|
-
createdBy:
|
|
1053
|
+
updatedBy: creatorEmail,
|
|
1054
|
+
createdBy: next.createdBy,
|
|
1055
|
+
visibility: input.visibility,
|
|
944
1056
|
});
|
|
945
1057
|
await recordAudit({
|
|
946
1058
|
action: "workspace-app.pending",
|
|
@@ -1420,6 +1532,8 @@ export async function scaffoldWorkspaceAppFromTemplate(input) {
|
|
|
1420
1532
|
}
|
|
1421
1533
|
const appId = (input.appId?.trim() || template).toLowerCase();
|
|
1422
1534
|
assertValidWorkspaceAppId(appId);
|
|
1535
|
+
await assertPendingWorkspaceAppCreationAvailable(appId);
|
|
1536
|
+
await assertWorkspaceAppIdRegisteredFree(appId);
|
|
1423
1537
|
const workspaceRoot = findWorkspaceRoot();
|
|
1424
1538
|
if (!workspaceRoot) {
|
|
1425
1539
|
throw new Error("No agent-native workspace detected for scaffolding.");
|
|
@@ -1428,10 +1542,35 @@ export async function scaffoldWorkspaceAppFromTemplate(input) {
|
|
|
1428
1542
|
if (fs.existsSync(appDir)) {
|
|
1429
1543
|
throw new Error(`apps/${appId} already exists.`);
|
|
1430
1544
|
}
|
|
1431
|
-
const
|
|
1432
|
-
|
|
1433
|
-
|
|
1434
|
-
|
|
1545
|
+
const visibility = await workspaceAppDefaultVisibility();
|
|
1546
|
+
const creatorEmail = currentOwnerEmail();
|
|
1547
|
+
let output;
|
|
1548
|
+
try {
|
|
1549
|
+
output = await runScaffoldCli({
|
|
1550
|
+
cwd: workspaceRoot,
|
|
1551
|
+
args: ["add-app", appId, "--template", template],
|
|
1552
|
+
});
|
|
1553
|
+
}
|
|
1554
|
+
catch (error) {
|
|
1555
|
+
if (fs.existsSync(appDir)) {
|
|
1556
|
+
fs.rmSync(appDir, { recursive: true, force: true });
|
|
1557
|
+
}
|
|
1558
|
+
throw error;
|
|
1559
|
+
}
|
|
1560
|
+
try {
|
|
1561
|
+
await writeWorkspaceAppMetadataOverride({
|
|
1562
|
+
appId,
|
|
1563
|
+
updatedBy: creatorEmail,
|
|
1564
|
+
createdBy: creatorEmail,
|
|
1565
|
+
visibility,
|
|
1566
|
+
});
|
|
1567
|
+
}
|
|
1568
|
+
catch (error) {
|
|
1569
|
+
if (fs.existsSync(appDir)) {
|
|
1570
|
+
fs.rmSync(appDir, { recursive: true, force: true });
|
|
1571
|
+
}
|
|
1572
|
+
throw error;
|
|
1573
|
+
}
|
|
1435
1574
|
await recordAudit({
|
|
1436
1575
|
action: "workspace-app.scaffolded",
|
|
1437
1576
|
targetType: "workspace-app",
|
|
@@ -1452,6 +1591,7 @@ export async function scaffoldWorkspaceAppFromTemplate(input) {
|
|
|
1452
1591
|
protectedPaths: [],
|
|
1453
1592
|
status: "ready",
|
|
1454
1593
|
createdBy: currentOwnerEmail(),
|
|
1594
|
+
visibility,
|
|
1455
1595
|
},
|
|
1456
1596
|
]);
|
|
1457
1597
|
return { appId, template, output };
|
|
@@ -1826,6 +1966,8 @@ export async function startWorkspaceAppCreation(input) {
|
|
|
1826
1966
|
};
|
|
1827
1967
|
}
|
|
1828
1968
|
}
|
|
1969
|
+
const creationVisibility = await workspaceAppDefaultVisibility();
|
|
1970
|
+
await assertPendingWorkspaceAppCreationAvailable(initial.appId);
|
|
1829
1971
|
const selectedKeys = input.secretIds?.length
|
|
1830
1972
|
? (await listSecretOptions())
|
|
1831
1973
|
.filter((secret) => input.secretIds?.includes(secret.id))
|
|
@@ -1844,6 +1986,15 @@ export async function startWorkspaceAppCreation(input) {
|
|
|
1844
1986
|
const appDescription = input.description?.trim() ||
|
|
1845
1987
|
generateWorkspaceAppDescription(input.prompt, built.appId);
|
|
1846
1988
|
if (isLocal) {
|
|
1989
|
+
await writeWorkspaceAppMetadataOverride({
|
|
1990
|
+
appId: built.appId,
|
|
1991
|
+
description: appDescription,
|
|
1992
|
+
generated: true,
|
|
1993
|
+
sourcePrompt: input.prompt,
|
|
1994
|
+
updatedBy: currentOwnerEmail(),
|
|
1995
|
+
createdBy: currentOwnerEmail(),
|
|
1996
|
+
visibility: creationVisibility,
|
|
1997
|
+
});
|
|
1847
1998
|
await requestSelectedVaultKeys({
|
|
1848
1999
|
appId: built.appId,
|
|
1849
2000
|
selectedKeys,
|
|
@@ -1920,6 +2071,12 @@ export async function startWorkspaceAppCreation(input) {
|
|
|
1920
2071
|
}
|
|
1921
2072
|
}
|
|
1922
2073
|
const builderUserId = builderCreds.userId || undefined;
|
|
2074
|
+
await reservePendingWorkspaceApp({
|
|
2075
|
+
appId: built.appId,
|
|
2076
|
+
description: appDescription,
|
|
2077
|
+
projectId: builderProjectId,
|
|
2078
|
+
visibility: creationVisibility,
|
|
2079
|
+
});
|
|
1923
2080
|
let result;
|
|
1924
2081
|
try {
|
|
1925
2082
|
result = normalizeBuilderRunResult(await runBuilderAgent({
|
|
@@ -1931,6 +2088,12 @@ export async function startWorkspaceAppCreation(input) {
|
|
|
1931
2088
|
}));
|
|
1932
2089
|
}
|
|
1933
2090
|
catch (err) {
|
|
2091
|
+
try {
|
|
2092
|
+
await releasePendingWorkspaceAppReservation(built.appId);
|
|
2093
|
+
}
|
|
2094
|
+
catch (cleanupError) {
|
|
2095
|
+
console.warn("[dispatch] failed to release pending workspace app reservation", cleanupError);
|
|
2096
|
+
}
|
|
1934
2097
|
const detail = err instanceof Error && err.message
|
|
1935
2098
|
? err.message
|
|
1936
2099
|
: "Builder could not start the app branch";
|
|
@@ -1948,6 +2111,7 @@ export async function startWorkspaceAppCreation(input) {
|
|
|
1948
2111
|
projectId: builderProjectId,
|
|
1949
2112
|
description: appDescription,
|
|
1950
2113
|
sourcePrompt: input.prompt,
|
|
2114
|
+
visibility: creationVisibility,
|
|
1951
2115
|
branchName: result.branchName,
|
|
1952
2116
|
builderUrl: result.url,
|
|
1953
2117
|
});
|