@agent-native/core 0.136.4 → 0.137.0
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/corpus/README.md +1 -1
- package/corpus/templates/clips/.agents/skills/recording/SKILL.md +53 -0
- package/corpus/templates/clips/actions/import-loom-recording.ts +7 -0
- package/corpus/templates/clips/actions/lib/loom-import-job.ts +24 -4
- package/corpus/templates/clips/app/components/import-menu.tsx +109 -0
- package/corpus/templates/clips/app/components/library/library-grid.tsx +8 -18
- package/corpus/templates/clips/app/components/library/library-layout.tsx +14 -50
- package/corpus/templates/clips/app/components/library/sort-menu.tsx +20 -6
- package/corpus/templates/clips/app/components/recorder/pre-record-panel.tsx +10 -34
- package/corpus/templates/clips/app/routes/_app.library._index.tsx +0 -2
- package/corpus/templates/clips/changelog/2026-08-03-restart-during-a-recording-now-immediately-starts-a-fresh-ta.md +6 -0
- package/corpus/templates/clips/changelog/2026-08-04-cleaner-library-controls-with-an-icon-only-sort-button.md +6 -0
- package/corpus/templates/clips/changelog/2026-08-04-upload-and-loom-imports-now-live-under-one-import-menu.md +6 -0
- package/corpus/templates/clips/desktop/src/app.tsx +81 -30
- package/corpus/templates/clips/desktop/src/lib/recorder.ts +436 -158
- package/corpus/templates/clips/server/lib/post-finalize-dispatch.ts +14 -1
- package/corpus/templates/clips/server/plugins/auth.ts +9 -0
- package/corpus/templates/clips/server/routes/api/_agent-native-background/post-finalize-worker.post.ts +13 -0
- package/corpus/templates/content/actions/_database-utils.ts +6 -0
- package/corpus/templates/content/actions/bind-content-database-source-field.ts +273 -97
- package/corpus/templates/content/actions/change-content-database-source-role.ts +65 -29
- package/corpus/templates/content/actions/configure-document-property.ts +113 -32
- package/corpus/templates/content/actions/delete-content-database.ts +5 -7
- package/corpus/templates/content/actions/delete-document-property.ts +109 -62
- package/corpus/templates/content/actions/delete-document.ts +178 -36
- package/corpus/templates/content/actions/duplicate-database-item.ts +27 -0
- package/corpus/templates/content/actions/duplicate-database-items.ts +31 -1
- package/corpus/templates/content/actions/permanently-delete-document.ts +4 -6
- package/corpus/templates/content/actions/remove-database-items.ts +13 -0
- package/corpus/templates/content/actions/set-document-property.ts +69 -1
- package/corpus/templates/content/actions/upsert-database-item-by-key.ts +814 -0
- package/corpus/templates/content/parity/matrix.md +1 -1
- package/corpus/templates/content/parity/matrix.ts +1 -0
- package/corpus/templates/content/server/db/schema.ts +27 -0
- package/corpus/templates/content/server/plugins/db.ts +24 -0
- package/corpus/templates/design/app/components/design/BreakpointBar.tsx +122 -114
- package/corpus/templates/design/app/components/design/CanvasContextMenu.tsx +2 -2
- package/corpus/templates/design/app/components/design/EditPanel.tsx +68 -34
- package/corpus/templates/design/app/components/design/KeyboardShortcutsPanel.tsx +78 -4
- package/corpus/templates/design/app/components/design/MultiScreenCanvas.tsx +8 -0
- package/corpus/templates/design/app/components/design/QuestionFlow.tsx +5 -2
- package/corpus/templates/design/app/components/design/edit-panel/panel-primitives.tsx +10 -9
- package/corpus/templates/design/app/components/design/inspector/AutoLayoutMatrix.tsx +31 -69
- package/corpus/templates/design/app/components/design/inspector/design-icons.tsx +80 -106
- package/corpus/templates/design/app/components/design/multi-screen/types.ts +4 -0
- package/corpus/templates/design/app/hooks/use-editor-preferences.ts +75 -0
- package/corpus/templates/design/app/i18n-data.ts +8 -0
- package/corpus/templates/design/app/i18n-keyboard-shortcuts.ts +61 -0
- package/corpus/templates/design/app/pages/design-editor/editor-preferences.ts +98 -0
- package/corpus/templates/design/app/pages/design-editor/nudge-intent.ts +491 -0
- package/corpus/templates/design/app/pages/design-editor/paste-placement.ts +133 -0
- package/corpus/templates/design/app/pages/design-editor/png-export-render.ts +8 -0
- package/corpus/templates/design/app/pages/design-editor/selection-state.ts +5 -12
- package/corpus/templates/design/changelog/2026-08-03-arrow-keys-now-reorder-a-layer-inside-an-auto-layout-frame.md +6 -0
- package/corpus/templates/design/changelog/2026-08-03-auto-layout-flow-icons-show-the-direction-at-a-glance-instea.md +6 -0
- package/corpus/templates/design/changelog/2026-08-03-auto-layout-icons-share-one-outlined-square-style-so-flow-ga.md +6 -0
- package/corpus/templates/design/changelog/2026-08-03-export-has-a-named-preview-section-you-can-expand-replacing-.md +6 -0
- package/corpus/templates/design/changelog/2026-08-03-export-preview-shows-the-selected-element-instead-of-the-who.md +6 -0
- package/corpus/templates/design/changelog/2026-08-03-inspector-sections-show-an-expand-indicator-so-it-is-clear-w.md +6 -0
- package/corpus/templates/design/changelog/2026-08-03-paste-lands-inside-a-selected-frame-instead-of-beside-it.md +6 -0
- package/corpus/templates/design/changelog/2026-08-03-the-add-breakpoint-button-sits-outside-the-breakpoint-select.md +6 -0
- package/corpus/templates/design/changelog/2026-08-03-the-agent-s-follow-up-questions-keep-space-below-the-continu.md +6 -0
- package/corpus/templates/design/changelog/2026-08-03-the-agent-s-follow-up-questions-share-the-chat-panel-s-backg.md +6 -0
- package/corpus/templates/design/changelog/2026-08-03-the-export-panel-no-longer-stacks-a-duplicate-export-block-o.md +6 -0
- package/corpus/templates/design/changelog/2026-08-03-the-text-tool-uses-a-t-icon-matching-the-letter-it-inserts.md +6 -0
- package/corpus/templates/factory/.env.example +17 -0
- package/corpus/templates/factory/AGENTS.md +27 -17
- package/corpus/templates/factory/README.md +19 -9
- package/corpus/templates/factory/actions/get-slack-feedback-context.ts +90 -0
- package/corpus/templates/factory/actions/get-triage-config.ts +14 -3
- package/corpus/templates/factory/actions/govern-agent-native-pull-request.ts +386 -0
- package/corpus/templates/factory/actions/list-factory-automations.ts +53 -0
- package/corpus/templates/factory/actions/list-triage-items.ts +7 -2
- package/corpus/templates/factory/actions/poll-github-sources.ts +203 -0
- package/corpus/templates/factory/actions/poll-sentry-errors.ts +161 -0
- package/corpus/templates/factory/actions/poll-slack-channel.ts +4 -2
- package/corpus/templates/factory/actions/run-factory-automation.ts +42 -0
- package/corpus/templates/factory/actions/save-factory-automation.ts +69 -0
- package/corpus/templates/factory/actions/save-triage-config.ts +28 -2
- package/corpus/templates/factory/actions/start-builder-for-item.ts +411 -0
- package/corpus/templates/factory/app/components/factory/FactoryCanvas.tsx +14 -3
- package/corpus/templates/factory/app/components/ui/checkbox.tsx +1 -0
- package/corpus/templates/factory/app/hooks/use-navigation-state.ts +7 -0
- package/corpus/templates/factory/app/i18n/en-US.ts +35 -1
- package/corpus/templates/factory/app/routes/factory.tsx +462 -9
- package/corpus/templates/factory/changelog/2026-08-04-added-organization-owned-factory-automations-for-slack-bug-t.md +6 -0
- package/corpus/templates/factory/changelog/2026-08-04-automation-editor-controls-and-model-picker.md +6 -0
- package/corpus/templates/factory/netlify.toml +2 -2
- package/corpus/templates/factory/server/connectors/credentials.ts +6 -5
- package/corpus/templates/factory/server/connectors/slack.ts +133 -5
- package/corpus/templates/factory/server/db/schema.ts +6 -0
- package/corpus/templates/factory/server/lib/require-factory-automation.ts +65 -0
- package/corpus/templates/factory/server/plugins/agent-chat.ts +29 -11
- package/corpus/templates/factory/server/plugins/auth.ts +1 -0
- package/corpus/templates/factory/server/plugins/factory-migrations.ts +12 -0
- package/corpus/templates/factory/server/plugins/factory-scheduler-job.ts +218 -67
- package/corpus/templates/factory/server/triage/builder-executor.ts +4 -0
- package/corpus/templates/factory/server/triage/contracts.ts +14 -1
- package/corpus/templates/factory/server/triage/github-client.ts +404 -0
- package/corpus/templates/factory/server/triage/metadata.ts +44 -0
- package/corpus/templates/factory/server/triage/pr-babysit.ts +5 -5
- package/corpus/templates/factory/server/triage/pr-policy.ts +144 -0
- package/corpus/templates/factory/server/triage/sentry-client.ts +180 -0
- package/corpus/templates/factory/server/triage/slack-client.ts +43 -0
- package/corpus/templates/factory/server/triage/slack-poller.ts +9 -2
- package/corpus/templates/plan/.agents/skills/visual-plan/SKILL.md +27 -0
- package/corpus/templates/plan/.agents/skills/visual-plan/references/canvas.md +14 -4
- package/corpus/templates/plan/.agents/skills/visual-plan/references/wireframe.md +7 -0
- package/corpus/templates/plan/.agents/skills/visual-recap/references/wireframe.md +7 -0
- package/corpus/templates/plan/actions/create-plan-design.ts +8 -6
- package/corpus/templates/plan/actions/create-prototype-plan.ts +8 -6
- package/corpus/templates/plan/actions/create-ui-plan.ts +7 -7
- package/corpus/templates/plan/actions/get-plan-blocks.ts +7 -1
- package/corpus/templates/plan/actions/validate-plan-input.ts +39 -0
- package/corpus/templates/plan/app/components/plan/CanvasArea.tsx +167 -11
- package/corpus/templates/plan/app/i18n/en-US.ts +1 -0
- package/corpus/templates/plan/changelog/2026-08-04-canvas-zoom-controls-now-explain-the-command-ctrl-plus-scrol.md +6 -0
- package/corpus/templates/plan/changelog/2026-08-04-pinch-to-zoom-is-now-supported-on-touchscreens-in-visual-pla.md +6 -0
- package/corpus/templates/plan/shared/plan-content.ts +17 -2
- package/dist/cli/skills-content/canvas.d.ts +3 -3
- package/dist/cli/skills-content/canvas.js +16 -5
- package/dist/cli/skills-content/visual-plan-skill.d.ts +1 -1
- package/dist/cli/skills-content/visual-plan-skill.js +27 -0
- package/dist/cli/skills-content/wireframe.d.ts +2 -2
- package/dist/cli/skills-content/wireframe.js +7 -0
- package/dist/client/AgentPanel.js +23 -8
- package/dist/client/api-path.d.ts +6 -0
- package/dist/client/api-path.js +55 -0
- package/dist/client/onboarding/FirstRunOnboarding.d.ts +3 -0
- package/dist/client/onboarding/FirstRunOnboarding.js +93 -0
- package/dist/client/onboarding/first-run-enabled.d.ts +2 -0
- package/dist/client/onboarding/first-run-enabled.js +9 -0
- package/dist/client/onboarding/index.d.ts +2 -1
- package/dist/client/onboarding/index.js +1 -0
- package/dist/client/onboarding/use-onboarding.d.ts +6 -1
- package/dist/client/onboarding/use-onboarding.js +32 -1
- package/dist/client/route-state.js +10 -0
- package/dist/client/settings/SecretsSection.js +32 -8
- package/dist/collab/struct-routes.d.ts +1 -1
- package/dist/connections/catalog.d.ts +4 -4
- package/dist/connections/catalog.js +3 -3
- package/dist/db/index.d.ts +1 -1
- package/dist/db/index.js +1 -1
- package/dist/localization/default-messages.d.ts +5 -0
- package/dist/localization/default-messages.js +5 -0
- package/dist/mcp/screen-memory-stdio.d.ts +7 -7
- package/dist/observability/routes.d.ts +3 -3
- package/dist/onboarding/app-profile.d.ts +4 -0
- package/dist/onboarding/app-profile.js +378 -0
- package/dist/onboarding/index.d.ts +2 -1
- package/dist/onboarding/index.js +1 -0
- package/dist/onboarding/plugin.d.ts +2 -0
- package/dist/onboarding/plugin.js +21 -1
- package/dist/onboarding/types.d.ts +19 -0
- package/dist/progress/routes.d.ts +1 -1
- package/dist/provider-api/actions/custom-provider-registration.d.ts +9 -9
- package/dist/provider-api/actions/provider-api.d.ts +12 -12
- package/dist/secrets/routes.d.ts +2 -2
- package/dist/secrets/routes.js +41 -17
- package/dist/server/auth.js +21 -1
- package/dist/server/better-auth-instance.d.ts +3 -2
- package/dist/server/better-auth-instance.js +9 -7
- package/dist/server/email-markdown.js +3 -8
- package/dist/server/transcribe-voice.d.ts +1 -1
- package/dist/shared/first-run-onboarding.d.ts +3 -0
- package/dist/shared/first-run-onboarding.js +3 -0
- package/dist/triggers/index.d.ts +3 -0
- package/dist/triggers/index.js +5 -0
- package/docs/content/cross-app-sso.mdx +26 -0
- package/package.json +1 -1
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { useChatModels } from "@agent-native/core/client/agent-chat";
|
|
1
2
|
import {
|
|
2
3
|
useActionMutation,
|
|
3
4
|
useActionQuery,
|
|
@@ -26,6 +27,7 @@ import {
|
|
|
26
27
|
import { TriageStatusPill } from "@/components/triage/triage-status-pill";
|
|
27
28
|
import { Button } from "@/components/ui/button";
|
|
28
29
|
import { Card, CardContent, CardHeader, CardTitle } from "@/components/ui/card";
|
|
30
|
+
import { Checkbox } from "@/components/ui/checkbox";
|
|
29
31
|
import { Input } from "@/components/ui/input";
|
|
30
32
|
import { Label } from "@/components/ui/label";
|
|
31
33
|
import { Textarea } from "@/components/ui/textarea";
|
|
@@ -94,11 +96,41 @@ type TriageConfig = {
|
|
|
94
96
|
slackChannelId?: string | null;
|
|
95
97
|
slackChannelName?: string | null;
|
|
96
98
|
pollingEnabled?: boolean;
|
|
99
|
+
githubPollingEnabled?: boolean;
|
|
100
|
+
sentryPollingEnabled?: boolean;
|
|
101
|
+
sentryOrgSlug?: string | null;
|
|
102
|
+
sentryProjectSlug?: string | null;
|
|
103
|
+
sentryEnvironment?: string | null;
|
|
97
104
|
repository?: string | null;
|
|
98
105
|
};
|
|
99
106
|
|
|
100
107
|
type Verdict = "correct" | "incorrect" | "uncertain";
|
|
101
|
-
type WorkspaceTab = "map" | "inbox" | "rules" | "settings";
|
|
108
|
+
type WorkspaceTab = "map" | "inbox" | "rules" | "automations" | "settings";
|
|
109
|
+
|
|
110
|
+
type FactoryAutomationRun = {
|
|
111
|
+
id?: string;
|
|
112
|
+
status?: string | null;
|
|
113
|
+
startedAt?: string | number | null;
|
|
114
|
+
finishedAt?: string | number | null;
|
|
115
|
+
error?: string | null;
|
|
116
|
+
};
|
|
117
|
+
|
|
118
|
+
type FactoryAutomation = {
|
|
119
|
+
id: string;
|
|
120
|
+
name: string;
|
|
121
|
+
prompt?: string | null;
|
|
122
|
+
body?: string | null;
|
|
123
|
+
model?: string | null;
|
|
124
|
+
schedule?: string | null;
|
|
125
|
+
enabled: boolean;
|
|
126
|
+
triggerType?: string | null;
|
|
127
|
+
event?: string | null;
|
|
128
|
+
timezone?: string | null;
|
|
129
|
+
condition?: string | null;
|
|
130
|
+
canUpdate?: boolean;
|
|
131
|
+
runs?: FactoryAutomationRun[] | null;
|
|
132
|
+
pastRuns?: FactoryAutomationRun[] | null;
|
|
133
|
+
};
|
|
102
134
|
|
|
103
135
|
const DEFAULT_FACTORY_ID = "product-feedback";
|
|
104
136
|
|
|
@@ -387,7 +419,13 @@ export default function FactoryRoute() {
|
|
|
387
419
|
active={activeTab === "rules"}
|
|
388
420
|
onClick={() => setActiveTab("rules")}
|
|
389
421
|
>
|
|
390
|
-
|
|
422
|
+
{t("factoryRoute.rulesTab")}
|
|
423
|
+
</TabButton>
|
|
424
|
+
<TabButton
|
|
425
|
+
active={activeTab === "automations"}
|
|
426
|
+
onClick={() => setActiveTab("automations")}
|
|
427
|
+
>
|
|
428
|
+
{t("factoryRoute.automationsTab")}
|
|
391
429
|
</TabButton>
|
|
392
430
|
<TabButton
|
|
393
431
|
active={activeTab === "settings"}
|
|
@@ -467,6 +505,8 @@ export default function FactoryRoute() {
|
|
|
467
505
|
<InboxView t={t} />
|
|
468
506
|
) : activeTab === "rules" ? (
|
|
469
507
|
<RulesView t={t} />
|
|
508
|
+
) : activeTab === "automations" ? (
|
|
509
|
+
<AutomationsView factoryId={factoryId} t={t} />
|
|
470
510
|
) : (
|
|
471
511
|
<SettingsView t={t} />
|
|
472
512
|
)}
|
|
@@ -497,7 +537,10 @@ function TabButton({
|
|
|
497
537
|
}
|
|
498
538
|
|
|
499
539
|
function parseWorkspaceTab(value: string | null): WorkspaceTab {
|
|
500
|
-
return value === "inbox" ||
|
|
540
|
+
return value === "inbox" ||
|
|
541
|
+
value === "rules" ||
|
|
542
|
+
value === "automations" ||
|
|
543
|
+
value === "settings"
|
|
501
544
|
? value
|
|
502
545
|
: "map";
|
|
503
546
|
}
|
|
@@ -513,6 +556,356 @@ function Metric({ label, value }: { label: string; value: number }) {
|
|
|
513
556
|
);
|
|
514
557
|
}
|
|
515
558
|
|
|
559
|
+
function AutomationsView({
|
|
560
|
+
factoryId,
|
|
561
|
+
t,
|
|
562
|
+
}: {
|
|
563
|
+
factoryId: string;
|
|
564
|
+
t: ReturnType<typeof useT>;
|
|
565
|
+
}) {
|
|
566
|
+
const [searchParams, setSearchParams] = useSearchParams();
|
|
567
|
+
const [draft, setDraft] = useState<FactoryAutomation | null>(null);
|
|
568
|
+
const selectedId = searchParams.get("automationId");
|
|
569
|
+
const automationsQuery = useActionQuery<FactoryAutomation[]>(
|
|
570
|
+
"list-factory-automations",
|
|
571
|
+
{ factoryId },
|
|
572
|
+
);
|
|
573
|
+
const saveMutation = useActionMutation("save-factory-automation");
|
|
574
|
+
const runMutation = useActionMutation("run-factory-automation");
|
|
575
|
+
const {
|
|
576
|
+
availableModels,
|
|
577
|
+
defaultModel,
|
|
578
|
+
isLoading: modelsLoading,
|
|
579
|
+
} = useChatModels({ storageKey: null });
|
|
580
|
+
const response = automationsQuery.data;
|
|
581
|
+
const automations = response ?? [];
|
|
582
|
+
const selected =
|
|
583
|
+
automations.find((automation) => automation.id === selectedId) ??
|
|
584
|
+
automations[0] ??
|
|
585
|
+
null;
|
|
586
|
+
const modelOptions = useMemo(() => {
|
|
587
|
+
const configuredGroups = availableModels.filter(
|
|
588
|
+
(group) => group.configured,
|
|
589
|
+
);
|
|
590
|
+
const groups =
|
|
591
|
+
configuredGroups.length > 0 ? configuredGroups : availableModels;
|
|
592
|
+
const seen = new Set<string>();
|
|
593
|
+
return groups.flatMap((group) =>
|
|
594
|
+
group.models.flatMap((model) => {
|
|
595
|
+
if (model === "auto" || seen.has(model)) return [];
|
|
596
|
+
seen.add(model);
|
|
597
|
+
return [
|
|
598
|
+
{ value: model, label: `${group.label} / ${formatModelName(model)}` },
|
|
599
|
+
];
|
|
600
|
+
}),
|
|
601
|
+
);
|
|
602
|
+
}, [availableModels]);
|
|
603
|
+
const autoModelLabel = `Auto (currently ${formatModelName(defaultModel)})`;
|
|
604
|
+
const activeAutomationId = selected?.id ?? null;
|
|
605
|
+
|
|
606
|
+
function draftForAutomation(automation: FactoryAutomation) {
|
|
607
|
+
return { ...automation, model: automation.model?.trim() || "auto" };
|
|
608
|
+
}
|
|
609
|
+
|
|
610
|
+
function selectAutomation(id: string) {
|
|
611
|
+
const nextAutomation = automations.find(
|
|
612
|
+
(automation) => automation.id === id,
|
|
613
|
+
);
|
|
614
|
+
if (nextAutomation) setDraft(draftForAutomation(nextAutomation));
|
|
615
|
+
setSearchParams(
|
|
616
|
+
(current) => {
|
|
617
|
+
const next = new URLSearchParams(current);
|
|
618
|
+
next.set("automationId", id);
|
|
619
|
+
return next;
|
|
620
|
+
},
|
|
621
|
+
{ replace: true },
|
|
622
|
+
);
|
|
623
|
+
}
|
|
624
|
+
|
|
625
|
+
useEffect(() => {
|
|
626
|
+
if (!selected) {
|
|
627
|
+
setDraft(null);
|
|
628
|
+
return;
|
|
629
|
+
}
|
|
630
|
+
if (selected.id !== selectedId) {
|
|
631
|
+
selectAutomation(selected.id);
|
|
632
|
+
return;
|
|
633
|
+
}
|
|
634
|
+
setDraft(draftForAutomation(selected));
|
|
635
|
+
}, [selected, selectedId]);
|
|
636
|
+
|
|
637
|
+
async function saveAutomation() {
|
|
638
|
+
if (!draft) return;
|
|
639
|
+
await saveMutation.mutateAsync({
|
|
640
|
+
factoryId,
|
|
641
|
+
automationId: draft.id,
|
|
642
|
+
name: draft.name,
|
|
643
|
+
prompt: draft.prompt ?? draft.body ?? "",
|
|
644
|
+
model: draft.model ?? "",
|
|
645
|
+
schedule: draft.schedule ?? "",
|
|
646
|
+
enabled: draft.enabled,
|
|
647
|
+
});
|
|
648
|
+
await automationsQuery.refetch();
|
|
649
|
+
}
|
|
650
|
+
|
|
651
|
+
async function runAutomation() {
|
|
652
|
+
if (!draft) return;
|
|
653
|
+
await runMutation.mutateAsync({ factoryId, automationId: draft.id });
|
|
654
|
+
await automationsQuery.refetch();
|
|
655
|
+
}
|
|
656
|
+
|
|
657
|
+
return (
|
|
658
|
+
<div className="grid gap-4 p-4 lg:grid-cols-[minmax(220px,.35fr)_minmax(0,1fr)] lg:p-6">
|
|
659
|
+
<Card>
|
|
660
|
+
<CardHeader>
|
|
661
|
+
<CardTitle className="text-base">
|
|
662
|
+
{t("factoryRoute.automationsTitle")}
|
|
663
|
+
</CardTitle>
|
|
664
|
+
<p className="text-sm text-muted-foreground">
|
|
665
|
+
{t("factoryRoute.automationsDescription")}
|
|
666
|
+
</p>
|
|
667
|
+
</CardHeader>
|
|
668
|
+
<CardContent className="p-0">
|
|
669
|
+
{automationsQuery.isLoading ? (
|
|
670
|
+
<p className="p-4 text-sm text-muted-foreground">
|
|
671
|
+
{t("factoryRoute.automationsLoading")}
|
|
672
|
+
</p>
|
|
673
|
+
) : automations.length === 0 ? (
|
|
674
|
+
<p className="p-4 text-sm text-muted-foreground">
|
|
675
|
+
{t("factoryRoute.automationsEmpty")}
|
|
676
|
+
</p>
|
|
677
|
+
) : (
|
|
678
|
+
<div
|
|
679
|
+
className="divide-y"
|
|
680
|
+
role="tablist"
|
|
681
|
+
aria-label={t("factoryRoute.automationsTitle")}
|
|
682
|
+
>
|
|
683
|
+
{automations.map((automation) => (
|
|
684
|
+
<button
|
|
685
|
+
key={automation.id}
|
|
686
|
+
type="button"
|
|
687
|
+
id={`factory-automation-tab-${automation.id}`}
|
|
688
|
+
role="tab"
|
|
689
|
+
aria-selected={activeAutomationId === automation.id}
|
|
690
|
+
aria-controls="factory-automation-panel"
|
|
691
|
+
className={`w-full cursor-pointer p-4 text-left transition-colors hover:bg-muted/50 focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-inset focus-visible:ring-ring ${activeAutomationId === automation.id ? "bg-muted/60" : ""}`}
|
|
692
|
+
onClick={() => selectAutomation(automation.id)}
|
|
693
|
+
>
|
|
694
|
+
<span className="block truncate text-sm font-medium">
|
|
695
|
+
{automation.name}
|
|
696
|
+
</span>
|
|
697
|
+
<span className="mt-1 block text-xs text-muted-foreground">
|
|
698
|
+
{automation.enabled
|
|
699
|
+
? t("factoryRoute.automationEnabled")
|
|
700
|
+
: t("factoryRoute.automationDisabled")}
|
|
701
|
+
</span>
|
|
702
|
+
</button>
|
|
703
|
+
))}
|
|
704
|
+
</div>
|
|
705
|
+
)}
|
|
706
|
+
</CardContent>
|
|
707
|
+
</Card>
|
|
708
|
+
|
|
709
|
+
<Card
|
|
710
|
+
id="factory-automation-panel"
|
|
711
|
+
role="tabpanel"
|
|
712
|
+
aria-labelledby={
|
|
713
|
+
activeAutomationId
|
|
714
|
+
? `factory-automation-tab-${activeAutomationId}`
|
|
715
|
+
: undefined
|
|
716
|
+
}
|
|
717
|
+
>
|
|
718
|
+
<CardHeader className="border-b sm:flex-row sm:items-center sm:justify-between">
|
|
719
|
+
<div>
|
|
720
|
+
<CardTitle className="text-base">
|
|
721
|
+
{t("factoryRoute.automationEditorTitle")}
|
|
722
|
+
</CardTitle>
|
|
723
|
+
<p className="mt-1 text-sm text-muted-foreground">
|
|
724
|
+
{t("factoryRoute.automationEditorDescription")}
|
|
725
|
+
</p>
|
|
726
|
+
</div>
|
|
727
|
+
{draft && (
|
|
728
|
+
<div className="flex gap-2">
|
|
729
|
+
<Button
|
|
730
|
+
type="button"
|
|
731
|
+
variant="outline"
|
|
732
|
+
size="sm"
|
|
733
|
+
onClick={() => void runAutomation()}
|
|
734
|
+
disabled={runMutation.isPending || draft.canUpdate === false}
|
|
735
|
+
>
|
|
736
|
+
{runMutation.isPending && (
|
|
737
|
+
<IconLoader2 className="animate-spin" />
|
|
738
|
+
)}
|
|
739
|
+
<IconPlayerPlay className="size-4" />
|
|
740
|
+
{t("factoryRoute.runNow")}
|
|
741
|
+
</Button>
|
|
742
|
+
<Button
|
|
743
|
+
type="button"
|
|
744
|
+
size="sm"
|
|
745
|
+
onClick={() => void saveAutomation()}
|
|
746
|
+
disabled={saveMutation.isPending || draft.canUpdate === false}
|
|
747
|
+
>
|
|
748
|
+
{saveMutation.isPending && (
|
|
749
|
+
<IconLoader2 className="animate-spin" />
|
|
750
|
+
)}
|
|
751
|
+
{t("factoryRoute.saveAutomation")}
|
|
752
|
+
</Button>
|
|
753
|
+
</div>
|
|
754
|
+
)}
|
|
755
|
+
</CardHeader>
|
|
756
|
+
<CardContent className="space-y-5 pt-5">
|
|
757
|
+
{!draft ? (
|
|
758
|
+
<p className="text-sm text-muted-foreground">
|
|
759
|
+
{t("factoryRoute.selectAutomation")}
|
|
760
|
+
</p>
|
|
761
|
+
) : (
|
|
762
|
+
<>
|
|
763
|
+
<div className="grid gap-4 md:grid-cols-2">
|
|
764
|
+
<div className="grid gap-1.5">
|
|
765
|
+
<Label htmlFor="factory-automation-model">
|
|
766
|
+
{t("factoryRoute.automationModel")}
|
|
767
|
+
</Label>
|
|
768
|
+
<select
|
|
769
|
+
id="factory-automation-model"
|
|
770
|
+
value={draft.model ?? ""}
|
|
771
|
+
onChange={(event) =>
|
|
772
|
+
setDraft({ ...draft, model: event.target.value })
|
|
773
|
+
}
|
|
774
|
+
disabled={modelsLoading && modelOptions.length === 0}
|
|
775
|
+
className="h-9 rounded-md border bg-background px-3 text-sm"
|
|
776
|
+
>
|
|
777
|
+
<option value="auto">{autoModelLabel}</option>
|
|
778
|
+
{draft.model &&
|
|
779
|
+
draft.model !== "auto" &&
|
|
780
|
+
!modelOptions.some(
|
|
781
|
+
(option) => option.value === draft.model,
|
|
782
|
+
) && (
|
|
783
|
+
<option value={draft.model}>
|
|
784
|
+
Configured / {formatModelName(draft.model)}
|
|
785
|
+
</option>
|
|
786
|
+
)}
|
|
787
|
+
{modelOptions.map((option) => (
|
|
788
|
+
<option key={option.value} value={option.value}>
|
|
789
|
+
{option.label}
|
|
790
|
+
</option>
|
|
791
|
+
))}
|
|
792
|
+
</select>
|
|
793
|
+
</div>
|
|
794
|
+
<div className="grid gap-1.5">
|
|
795
|
+
<Label htmlFor="factory-automation-schedule">
|
|
796
|
+
{t("factoryRoute.automationSchedule")}
|
|
797
|
+
</Label>
|
|
798
|
+
<Input
|
|
799
|
+
id="factory-automation-schedule"
|
|
800
|
+
value={draft.schedule ?? ""}
|
|
801
|
+
onChange={(event) =>
|
|
802
|
+
setDraft({ ...draft, schedule: event.target.value })
|
|
803
|
+
}
|
|
804
|
+
placeholder={t(
|
|
805
|
+
"factoryRoute.automationSchedulePlaceholder",
|
|
806
|
+
)}
|
|
807
|
+
/>
|
|
808
|
+
</div>
|
|
809
|
+
</div>
|
|
810
|
+
<label className="flex items-center gap-2 text-sm">
|
|
811
|
+
<Checkbox
|
|
812
|
+
checked={draft.enabled}
|
|
813
|
+
onCheckedChange={(checked) =>
|
|
814
|
+
setDraft({ ...draft, enabled: checked === true })
|
|
815
|
+
}
|
|
816
|
+
disabled={draft.canUpdate === false}
|
|
817
|
+
/>
|
|
818
|
+
{t("factoryRoute.automationEnabledLabel")}
|
|
819
|
+
</label>
|
|
820
|
+
<div className="grid gap-2 rounded-md bg-muted/60 p-3 text-sm md:grid-cols-3">
|
|
821
|
+
<div>
|
|
822
|
+
<span className="block text-xs text-muted-foreground">
|
|
823
|
+
{t("factoryRoute.automationTrigger")}
|
|
824
|
+
</span>
|
|
825
|
+
<span>{draft.triggerType ?? "-"}</span>
|
|
826
|
+
</div>
|
|
827
|
+
<div>
|
|
828
|
+
<span className="block text-xs text-muted-foreground">
|
|
829
|
+
{t("factoryRoute.automationEvent")}
|
|
830
|
+
</span>
|
|
831
|
+
<span>{draft.event ?? "-"}</span>
|
|
832
|
+
</div>
|
|
833
|
+
<div>
|
|
834
|
+
<span className="block text-xs text-muted-foreground">
|
|
835
|
+
{t("factoryRoute.automationTimezone")}
|
|
836
|
+
</span>
|
|
837
|
+
<span>{draft.timezone ?? "-"}</span>
|
|
838
|
+
</div>
|
|
839
|
+
</div>
|
|
840
|
+
<div className="grid gap-1.5">
|
|
841
|
+
<Label>{t("factoryRoute.automationPrompt")}</Label>
|
|
842
|
+
<Textarea
|
|
843
|
+
value={draft.prompt ?? draft.body ?? ""}
|
|
844
|
+
onChange={(event) =>
|
|
845
|
+
setDraft({ ...draft, prompt: event.target.value })
|
|
846
|
+
}
|
|
847
|
+
placeholder={t("factoryRoute.automationPromptPlaceholder")}
|
|
848
|
+
rows={12}
|
|
849
|
+
/>
|
|
850
|
+
<p className="text-right text-xs text-muted-foreground">
|
|
851
|
+
{t("factoryRoute.promptEditorHint")}
|
|
852
|
+
</p>
|
|
853
|
+
</div>
|
|
854
|
+
<div className="border-t pt-5">
|
|
855
|
+
<h3 className="text-sm font-medium">
|
|
856
|
+
{t("factoryRoute.pastRuns")}
|
|
857
|
+
</h3>
|
|
858
|
+
{(draft.runs ?? draft.pastRuns ?? []).length === 0 ? (
|
|
859
|
+
<p className="mt-2 text-sm text-muted-foreground">
|
|
860
|
+
{t("factoryRoute.pastRunsEmpty")}
|
|
861
|
+
</p>
|
|
862
|
+
) : (
|
|
863
|
+
<div className="mt-3 divide-y rounded-md border">
|
|
864
|
+
{(draft.runs ?? draft.pastRuns ?? []).map((run, index) => (
|
|
865
|
+
<div
|
|
866
|
+
key={run.id ?? `${draft.id}-run-${index}`}
|
|
867
|
+
className="flex flex-wrap items-center justify-between gap-2 px-3 py-2 text-sm"
|
|
868
|
+
>
|
|
869
|
+
<span className="font-medium">{run.status ?? "-"}</span>
|
|
870
|
+
<span className="text-xs text-muted-foreground">
|
|
871
|
+
{formatAutomationDate(run.startedAt)}
|
|
872
|
+
</span>
|
|
873
|
+
{run.error && (
|
|
874
|
+
<span className="basis-full text-xs text-destructive">
|
|
875
|
+
{run.error}
|
|
876
|
+
</span>
|
|
877
|
+
)}
|
|
878
|
+
</div>
|
|
879
|
+
))}
|
|
880
|
+
</div>
|
|
881
|
+
)}
|
|
882
|
+
</div>
|
|
883
|
+
</>
|
|
884
|
+
)}
|
|
885
|
+
</CardContent>
|
|
886
|
+
</Card>
|
|
887
|
+
</div>
|
|
888
|
+
);
|
|
889
|
+
}
|
|
890
|
+
|
|
891
|
+
function formatAutomationDate(value: string | number | null | undefined) {
|
|
892
|
+
if (!value) return "-";
|
|
893
|
+
const date = new Date(value);
|
|
894
|
+
return Number.isNaN(date.getTime()) ? String(value) : date.toLocaleString();
|
|
895
|
+
}
|
|
896
|
+
|
|
897
|
+
function formatModelName(model: string | null | undefined) {
|
|
898
|
+
const value = model?.trim();
|
|
899
|
+
if (!value) return "the app default";
|
|
900
|
+
const match = value.match(/^(?:openai\/)?gpt-5[.-]6[.-](sol|terra|luna)$/i);
|
|
901
|
+
if (match) return `GPT-5.6 ${match[1][0].toUpperCase()}${match[1].slice(1)}`;
|
|
902
|
+
return value
|
|
903
|
+
.split(/[./_-]+/)
|
|
904
|
+
.filter(Boolean)
|
|
905
|
+
.map((part) => part[0].toUpperCase() + part.slice(1))
|
|
906
|
+
.join(" ");
|
|
907
|
+
}
|
|
908
|
+
|
|
516
909
|
function InboxView({ t }: { t: ReturnType<typeof useT> }) {
|
|
517
910
|
const [status, setStatus] = useState("");
|
|
518
911
|
const [selectedId, setSelectedId] = useState<string | null>(null);
|
|
@@ -882,6 +1275,11 @@ function SettingsView({ t }: { t: ReturnType<typeof useT> }) {
|
|
|
882
1275
|
const [channelName, setChannelName] = useState("");
|
|
883
1276
|
const [repository, setRepository] = useState("");
|
|
884
1277
|
const [polling, setPolling] = useState(false);
|
|
1278
|
+
const [githubPolling, setGithubPolling] = useState(false);
|
|
1279
|
+
const [sentryPolling, setSentryPolling] = useState(false);
|
|
1280
|
+
const [sentryOrgSlug, setSentryOrgSlug] = useState("");
|
|
1281
|
+
const [sentryProjectSlug, setSentryProjectSlug] = useState("");
|
|
1282
|
+
const [sentryEnvironment, setSentryEnvironment] = useState("");
|
|
885
1283
|
const query = useActionQuery("get-triage-config", {});
|
|
886
1284
|
const mutation = useActionMutation("save-triage-config");
|
|
887
1285
|
useEffect(() => {
|
|
@@ -892,6 +1290,11 @@ function SettingsView({ t }: { t: ReturnType<typeof useT> }) {
|
|
|
892
1290
|
setChannelName(data.slackChannelName ?? "");
|
|
893
1291
|
setRepository(data.repository ?? "");
|
|
894
1292
|
setPolling(data.pollingEnabled ?? false);
|
|
1293
|
+
setGithubPolling(data.githubPollingEnabled ?? false);
|
|
1294
|
+
setSentryPolling(data.sentryPollingEnabled ?? false);
|
|
1295
|
+
setSentryOrgSlug(data.sentryOrgSlug ?? "");
|
|
1296
|
+
setSentryProjectSlug(data.sentryProjectSlug ?? "");
|
|
1297
|
+
setSentryEnvironment(data.sentryEnvironment ?? "");
|
|
895
1298
|
}, [query.data]);
|
|
896
1299
|
return (
|
|
897
1300
|
<div className="max-w-4xl p-4 lg:p-6">
|
|
@@ -944,7 +1347,7 @@ function SettingsView({ t }: { t: ReturnType<typeof useT> }) {
|
|
|
944
1347
|
/>
|
|
945
1348
|
</div>
|
|
946
1349
|
<div className="grid gap-1.5">
|
|
947
|
-
<Label htmlFor="factory-repository">
|
|
1350
|
+
<Label htmlFor="factory-repository">{t("triage.repository")}</Label>
|
|
948
1351
|
<Input
|
|
949
1352
|
id="factory-repository"
|
|
950
1353
|
value={repository}
|
|
@@ -953,14 +1356,59 @@ function SettingsView({ t }: { t: ReturnType<typeof useT> }) {
|
|
|
953
1356
|
/>
|
|
954
1357
|
</div>
|
|
955
1358
|
<label className="flex items-center gap-2 text-sm sm:col-span-2">
|
|
956
|
-
<
|
|
957
|
-
type="checkbox"
|
|
1359
|
+
<Checkbox
|
|
958
1360
|
checked={polling}
|
|
959
|
-
|
|
960
|
-
className="size-4 rounded border"
|
|
1361
|
+
onCheckedChange={(checked) => setPolling(checked === true)}
|
|
961
1362
|
/>
|
|
962
1363
|
{t("triage.enablePolling")}
|
|
963
1364
|
</label>
|
|
1365
|
+
<label className="flex items-center gap-2 text-sm sm:col-span-2">
|
|
1366
|
+
<Checkbox
|
|
1367
|
+
checked={githubPolling}
|
|
1368
|
+
onCheckedChange={(checked) => setGithubPolling(checked === true)}
|
|
1369
|
+
/>
|
|
1370
|
+
{t("triage.enableGithubPolling")}
|
|
1371
|
+
</label>
|
|
1372
|
+
<div className="grid gap-1.5">
|
|
1373
|
+
<Label htmlFor="factory-sentry-org">
|
|
1374
|
+
{t("triage.sentryOrgSlug")}
|
|
1375
|
+
</Label>
|
|
1376
|
+
<Input
|
|
1377
|
+
id="factory-sentry-org"
|
|
1378
|
+
value={sentryOrgSlug}
|
|
1379
|
+
onChange={(event) => setSentryOrgSlug(event.target.value)}
|
|
1380
|
+
placeholder={t("triage.sentryOrgPlaceholder")}
|
|
1381
|
+
/>
|
|
1382
|
+
</div>
|
|
1383
|
+
<div className="grid gap-1.5">
|
|
1384
|
+
<Label htmlFor="factory-sentry-project">
|
|
1385
|
+
{t("triage.sentryProjectSlug")}
|
|
1386
|
+
</Label>
|
|
1387
|
+
<Input
|
|
1388
|
+
id="factory-sentry-project"
|
|
1389
|
+
value={sentryProjectSlug}
|
|
1390
|
+
onChange={(event) => setSentryProjectSlug(event.target.value)}
|
|
1391
|
+
placeholder={t("triage.sentryProjectPlaceholder")}
|
|
1392
|
+
/>
|
|
1393
|
+
</div>
|
|
1394
|
+
<div className="grid gap-1.5">
|
|
1395
|
+
<Label htmlFor="factory-sentry-environment">
|
|
1396
|
+
{t("triage.sentryEnvironment")}
|
|
1397
|
+
</Label>
|
|
1398
|
+
<Input
|
|
1399
|
+
id="factory-sentry-environment"
|
|
1400
|
+
value={sentryEnvironment}
|
|
1401
|
+
onChange={(event) => setSentryEnvironment(event.target.value)}
|
|
1402
|
+
placeholder={t("triage.sentryEnvironmentPlaceholder")}
|
|
1403
|
+
/>
|
|
1404
|
+
</div>
|
|
1405
|
+
<label className="flex items-center gap-2 text-sm sm:col-span-2">
|
|
1406
|
+
<Checkbox
|
|
1407
|
+
checked={sentryPolling}
|
|
1408
|
+
onCheckedChange={(checked) => setSentryPolling(checked === true)}
|
|
1409
|
+
/>
|
|
1410
|
+
{t("triage.enableSentryPolling")}
|
|
1411
|
+
</label>
|
|
964
1412
|
<div className="sm:col-span-2">
|
|
965
1413
|
<Button
|
|
966
1414
|
onClick={() =>
|
|
@@ -970,12 +1418,17 @@ function SettingsView({ t }: { t: ReturnType<typeof useT> }) {
|
|
|
970
1418
|
slackChannelName: channelName,
|
|
971
1419
|
repository,
|
|
972
1420
|
pollingEnabled: polling,
|
|
1421
|
+
githubPollingEnabled: githubPolling,
|
|
1422
|
+
sentryPollingEnabled: sentryPolling,
|
|
1423
|
+
sentryOrgSlug,
|
|
1424
|
+
sentryProjectSlug,
|
|
1425
|
+
sentryEnvironment,
|
|
973
1426
|
})
|
|
974
1427
|
}
|
|
975
1428
|
disabled={mutation.isPending}
|
|
976
1429
|
>
|
|
977
1430
|
{mutation.isPending && <IconLoader2 className="animate-spin" />}
|
|
978
|
-
|
|
1431
|
+
{t("triage.saveSettings")}
|
|
979
1432
|
</Button>
|
|
980
1433
|
</div>
|
|
981
1434
|
</CardContent>
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
[build]
|
|
2
2
|
command = "NITRO_PRESET=netlify pnpm --filter factory build"
|
|
3
|
-
publish = "
|
|
4
|
-
functions = "
|
|
3
|
+
publish = "dist"
|
|
4
|
+
functions = ".netlify/functions-internal"
|
|
5
5
|
|
|
6
6
|
[build.environment]
|
|
7
7
|
GA_MEASUREMENT_ID = "G-ESF7FYXGN9"
|
|
@@ -112,11 +112,6 @@ export async function resolveConnectorSecret(
|
|
|
112
112
|
if (legacySecret?.trim()) return legacySecret.trim();
|
|
113
113
|
}
|
|
114
114
|
|
|
115
|
-
// Deployment-managed connector configuration is the last resort inside the
|
|
116
|
-
// resolver, never a parallel path in a provider client.
|
|
117
|
-
const environmentSecret = process.env[key]?.trim(); // guard:allow-env-credential - deploy-level connector fallback
|
|
118
|
-
if (environmentSecret) return environmentSecret;
|
|
119
|
-
|
|
120
115
|
const vaultOrgId = getVaultOrgId();
|
|
121
116
|
if (vaultOrgId && !orgIds.includes(vaultOrgId)) {
|
|
122
117
|
for (const scope of ["org", "workspace"] as const) {
|
|
@@ -125,5 +120,11 @@ export async function resolveConnectorSecret(
|
|
|
125
120
|
}
|
|
126
121
|
}
|
|
127
122
|
|
|
123
|
+
// Deployment-managed connector configuration is the last resort inside the
|
|
124
|
+
// resolver, never a parallel path in a provider client. A designated
|
|
125
|
+
// Dispatch vault must win over a stale deployment copy of the same key.
|
|
126
|
+
const environmentSecret = process.env[key]?.trim(); // guard:allow-env-credential - deploy-level connector fallback
|
|
127
|
+
if (environmentSecret) return environmentSecret;
|
|
128
|
+
|
|
128
129
|
return undefined;
|
|
129
130
|
}
|