@growthub/cli 0.13.2 → 0.13.5
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/assets/worker-kits/growthub-custom-workspace-starter-v1/apps/workspace/app/api/workspace/metadata-graph/route.js +184 -0
- package/assets/worker-kits/growthub-custom-workspace-starter-v1/apps/workspace/app/api/workspace/refresh-sources/route.js +24 -2
- package/assets/worker-kits/growthub-custom-workspace-starter-v1/apps/workspace/app/api/workspace/route.js +14 -0
- package/assets/worker-kits/growthub-custom-workspace-starter-v1/apps/workspace/app/api/workspace/sandbox-agent-auth/login/route.js +74 -0
- package/assets/worker-kits/growthub-custom-workspace-starter-v1/apps/workspace/app/api/workspace/sandbox-agent-auth/logout/route.js +67 -0
- package/assets/worker-kits/growthub-custom-workspace-starter-v1/apps/workspace/app/api/workspace/sandbox-agent-auth/status/route.js +77 -0
- package/assets/worker-kits/growthub-custom-workspace-starter-v1/apps/workspace/app/api/workspace/sandbox-run/route.js +72 -4
- package/assets/worker-kits/growthub-custom-workspace-starter-v1/apps/workspace/app/data-model/components/AgentSwarmPanel.jsx +326 -0
- package/assets/worker-kits/growthub-custom-workspace-starter-v1/apps/workspace/app/data-model/components/DataModelShell.jsx +123 -27
- package/assets/worker-kits/growthub-custom-workspace-starter-v1/apps/workspace/app/data-model/components/OrchestrationGraphEmptyCanvas.jsx +6 -0
- package/assets/worker-kits/growthub-custom-workspace-starter-v1/apps/workspace/app/data-model/components/OrchestrationNodeConfigPanel.jsx +224 -1
- package/assets/worker-kits/growthub-custom-workspace-starter-v1/apps/workspace/app/data-model/components/OrchestrationRunTracePanel.jsx +754 -92
- package/assets/worker-kits/growthub-custom-workspace-starter-v1/apps/workspace/app/data-model/components/SandboxAgentAuthPanel.jsx +224 -0
- package/assets/worker-kits/growthub-custom-workspace-starter-v1/apps/workspace/app/data-model/components/SandboxRunPanel.jsx +32 -1
- package/assets/worker-kits/growthub-custom-workspace-starter-v1/apps/workspace/app/data-model/components/WorkspaceGraphInspectorPanel.jsx +226 -0
- package/assets/worker-kits/growthub-custom-workspace-starter-v1/apps/workspace/app/globals.css +530 -9
- package/assets/worker-kits/growthub-custom-workspace-starter-v1/apps/workspace/app/page.jsx +8 -1
- package/assets/worker-kits/growthub-custom-workspace-starter-v1/apps/workspace/app/settings/integrations/page.jsx +10 -7
- package/assets/worker-kits/growthub-custom-workspace-starter-v1/apps/workspace/app/workflows/RunSetupPanel.jsx +261 -0
- package/assets/worker-kits/growthub-custom-workspace-starter-v1/apps/workspace/app/workflows/WorkflowSurface.jsx +119 -9
- package/assets/worker-kits/growthub-custom-workspace-starter-v1/apps/workspace/app/workspace-builder.jsx +779 -138
- package/assets/worker-kits/growthub-custom-workspace-starter-v1/apps/workspace/app/workspace-rail.jsx +91 -14
- package/assets/worker-kits/growthub-custom-workspace-starter-v1/apps/workspace/docs/sandbox-environment-primitive.md +35 -0
- package/assets/worker-kits/growthub-custom-workspace-starter-v1/apps/workspace/lib/orchestration-agent-swarm.js +923 -0
- package/assets/worker-kits/growthub-custom-workspace-starter-v1/apps/workspace/lib/orchestration-graph-runner.js +28 -3
- package/assets/worker-kits/growthub-custom-workspace-starter-v1/apps/workspace/lib/orchestration-graph.js +216 -5
- package/assets/worker-kits/growthub-custom-workspace-starter-v1/apps/workspace/lib/orchestration-run-console.js +412 -0
- package/assets/worker-kits/growthub-custom-workspace-starter-v1/apps/workspace/lib/orchestration-run-inputs.js +366 -0
- package/assets/worker-kits/growthub-custom-workspace-starter-v1/apps/workspace/lib/orchestration-run-trace.js +34 -3
- package/assets/worker-kits/growthub-custom-workspace-starter-v1/apps/workspace/lib/sandbox-agent-auth-eligibility.js +50 -0
- package/assets/worker-kits/growthub-custom-workspace-starter-v1/apps/workspace/lib/sandbox-agent-auth-redaction.js +64 -0
- package/assets/worker-kits/growthub-custom-workspace-starter-v1/apps/workspace/lib/sandbox-agent-auth.js +665 -0
- package/assets/worker-kits/growthub-custom-workspace-starter-v1/apps/workspace/lib/sandbox-agent-host-catalog.js +168 -0
- package/assets/worker-kits/growthub-custom-workspace-starter-v1/apps/workspace/lib/workspace-chart-values.js +595 -0
- package/assets/worker-kits/growthub-custom-workspace-starter-v1/apps/workspace/lib/workspace-data-model.js +164 -7
- package/assets/worker-kits/growthub-custom-workspace-starter-v1/apps/workspace/lib/workspace-helper.js +11 -0
- package/assets/worker-kits/growthub-custom-workspace-starter-v1/apps/workspace/lib/workspace-metadata-graph.js +646 -0
- package/assets/worker-kits/growthub-custom-workspace-starter-v1/apps/workspace/lib/workspace-metadata-selectors.js +249 -0
- package/assets/worker-kits/growthub-custom-workspace-starter-v1/apps/workspace/lib/workspace-metadata-store.js +1186 -0
- package/assets/worker-kits/growthub-custom-workspace-starter-v1/apps/workspace/lib/workspace-schema.js +111 -1
- package/assets/worker-kits/growthub-custom-workspace-starter-v1/kit.json +14 -0
- package/package.json +1 -1
|
@@ -0,0 +1,184 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* GET /api/workspace/metadata-graph
|
|
3
|
+
*
|
|
4
|
+
* Growthub Workspace Metadata Graph V1 — read-only projection.
|
|
5
|
+
*
|
|
6
|
+
* Combines the governed workspace config and the live source-record sidecar
|
|
7
|
+
* into a typed metadata store and a node/edge graph. Consumers (the UI
|
|
8
|
+
* inspector, the workspace helper agent, and external operators) use this
|
|
9
|
+
* envelope to ask dependency questions without re-deriving widget/workflow
|
|
10
|
+
* contracts inside every component.
|
|
11
|
+
*
|
|
12
|
+
* Optional query parameters:
|
|
13
|
+
* - staleKind: "object" | "field" | "sourceRecord" | "workflow" | "agentHost" | "widget"
|
|
14
|
+
* - staleId: the corresponding metadata id (for `field`, use
|
|
15
|
+
* "<objectId>::<fieldId>")
|
|
16
|
+
*
|
|
17
|
+
* When both are provided the response `stale.groups` and `stale.reasons`
|
|
18
|
+
* reflect `selectStaleMetadataGroups({ kind, id })`. When omitted the
|
|
19
|
+
* stale section returns the empty baseline.
|
|
20
|
+
*
|
|
21
|
+
* Authority invariants:
|
|
22
|
+
* - GET only. PATCH / POST / PUT / DELETE are not exposed. Writes still
|
|
23
|
+
* flow through the existing governed routes
|
|
24
|
+
* (`PATCH /api/workspace`, `POST /api/workspace/refresh-sources`,
|
|
25
|
+
* `POST /api/workspace/sandbox-run`).
|
|
26
|
+
* - growthub.config.json remains the authoritative artifact.
|
|
27
|
+
* - No secrets are returned. Field metadata derived from secret-shaped
|
|
28
|
+
* column names is marked `isSecret: true` but no value is echoed.
|
|
29
|
+
* - Failures during read OR projection fall back to an empty store with
|
|
30
|
+
* warnings — this route never throws.
|
|
31
|
+
*/
|
|
32
|
+
|
|
33
|
+
import { NextResponse } from "next/server";
|
|
34
|
+
import { readWorkspaceConfig, readWorkspaceSourceRecords } from "@/lib/workspace-config";
|
|
35
|
+
import { buildWorkspaceMetadataStore } from "@/lib/workspace-metadata-store";
|
|
36
|
+
import { buildWorkspaceMetadataGraph } from "@/lib/workspace-metadata-graph";
|
|
37
|
+
import { selectStaleMetadataGroups } from "@/lib/workspace-metadata-selectors";
|
|
38
|
+
|
|
39
|
+
const ENVELOPE_KIND = "growthub-workspace-metadata-graph-v1";
|
|
40
|
+
const ENVELOPE_VERSION = 1;
|
|
41
|
+
|
|
42
|
+
function emptyMetadataStore() {
|
|
43
|
+
return {
|
|
44
|
+
kind: "growthub-workspace-metadata-store-v1",
|
|
45
|
+
version: 1,
|
|
46
|
+
objects: [],
|
|
47
|
+
fields: [],
|
|
48
|
+
views: [],
|
|
49
|
+
filters: [],
|
|
50
|
+
sorts: [],
|
|
51
|
+
widgets: [],
|
|
52
|
+
dashboards: [],
|
|
53
|
+
workflows: [],
|
|
54
|
+
workflowNodes: [],
|
|
55
|
+
workflowActions: [],
|
|
56
|
+
runInputs: [],
|
|
57
|
+
agentHosts: [],
|
|
58
|
+
sandboxes: [],
|
|
59
|
+
integrations: [],
|
|
60
|
+
integrationEntities: [],
|
|
61
|
+
sourceRecords: [],
|
|
62
|
+
runs: [],
|
|
63
|
+
outputArtifacts: [],
|
|
64
|
+
workerKits: [],
|
|
65
|
+
pipelineHealth: [],
|
|
66
|
+
warnings: []
|
|
67
|
+
};
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
async function GET(request) {
|
|
71
|
+
const warnings = [];
|
|
72
|
+
|
|
73
|
+
let workspaceConfig = null;
|
|
74
|
+
try {
|
|
75
|
+
workspaceConfig = await readWorkspaceConfig();
|
|
76
|
+
} catch (error) {
|
|
77
|
+
warnings.push(`Failed to read workspace config: ${error?.message || "unknown error"}`);
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
let workspaceSourceRecords = {};
|
|
81
|
+
try {
|
|
82
|
+
workspaceSourceRecords = (await readWorkspaceSourceRecords()) || {};
|
|
83
|
+
} catch (error) {
|
|
84
|
+
warnings.push(`Failed to read source records sidecar: ${error?.message || "unknown error"}`);
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
// Defensive: helpers are designed to never throw on partial/unknown input,
|
|
88
|
+
// but the route must remain HTTP-200 even if an unexpected exception bubbles
|
|
89
|
+
// up (so the UI inspector and agents always get a typed envelope).
|
|
90
|
+
let metadataStore;
|
|
91
|
+
try {
|
|
92
|
+
metadataStore = buildWorkspaceMetadataStore({
|
|
93
|
+
workspaceConfig: workspaceConfig || {},
|
|
94
|
+
workspaceSourceRecords
|
|
95
|
+
});
|
|
96
|
+
warnings.push(...metadataStore.warnings);
|
|
97
|
+
} catch (error) {
|
|
98
|
+
warnings.push(`Failed to build metadata store: ${error?.message || "unknown error"}`);
|
|
99
|
+
metadataStore = emptyMetadataStore();
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
let graph;
|
|
103
|
+
try {
|
|
104
|
+
graph = buildWorkspaceMetadataGraph(metadataStore);
|
|
105
|
+
warnings.push(...graph.warnings);
|
|
106
|
+
} catch (error) {
|
|
107
|
+
warnings.push(`Failed to build metadata graph: ${error?.message || "unknown error"}`);
|
|
108
|
+
graph = { kind: "growthub-workspace-metadata-graph-v1", version: 1, nodes: [], edges: [], warnings: [] };
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
// Optional stale-group selector via query params.
|
|
112
|
+
let staleGroups = [];
|
|
113
|
+
let staleReasons = [];
|
|
114
|
+
try {
|
|
115
|
+
const url = request && request.url ? new URL(request.url) : null;
|
|
116
|
+
const staleKind = url ? (url.searchParams.get("staleKind") || "").trim() : "";
|
|
117
|
+
const staleId = url ? (url.searchParams.get("staleId") || "").trim() : "";
|
|
118
|
+
if (staleKind && staleId) {
|
|
119
|
+
const result = selectStaleMetadataGroups(metadataStore, { kind: staleKind, id: staleId });
|
|
120
|
+
staleGroups = Array.isArray(result?.groups) ? result.groups : [];
|
|
121
|
+
staleReasons = Array.isArray(result?.reasons) ? result.reasons : [];
|
|
122
|
+
}
|
|
123
|
+
} catch (error) {
|
|
124
|
+
warnings.push(`Failed to compute stale groups: ${error?.message || "unknown error"}`);
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
return NextResponse.json({
|
|
128
|
+
kind: ENVELOPE_KIND,
|
|
129
|
+
version: ENVELOPE_VERSION,
|
|
130
|
+
authority: {
|
|
131
|
+
config: "growthub.config.json",
|
|
132
|
+
sourceRecords: "growthub.source-records.json",
|
|
133
|
+
readOnlyProjection: true
|
|
134
|
+
},
|
|
135
|
+
metadata: {
|
|
136
|
+
objects: metadataStore.objects,
|
|
137
|
+
fields: metadataStore.fields,
|
|
138
|
+
views: metadataStore.views,
|
|
139
|
+
filters: metadataStore.filters,
|
|
140
|
+
sorts: metadataStore.sorts,
|
|
141
|
+
widgets: metadataStore.widgets,
|
|
142
|
+
dashboards: metadataStore.dashboards,
|
|
143
|
+
workflows: metadataStore.workflows,
|
|
144
|
+
workflowNodes: metadataStore.workflowNodes,
|
|
145
|
+
workflowActions: metadataStore.workflowActions,
|
|
146
|
+
runInputs: metadataStore.runInputs,
|
|
147
|
+
agentHosts: metadataStore.agentHosts,
|
|
148
|
+
sandboxes: metadataStore.sandboxes,
|
|
149
|
+
integrations: metadataStore.integrations,
|
|
150
|
+
integrationEntities: metadataStore.integrationEntities,
|
|
151
|
+
sourceRecords: metadataStore.sourceRecords,
|
|
152
|
+
runs: metadataStore.runs,
|
|
153
|
+
outputArtifacts: metadataStore.outputArtifacts,
|
|
154
|
+
workerKits: metadataStore.workerKits,
|
|
155
|
+
pipelineHealth: metadataStore.pipelineHealth
|
|
156
|
+
},
|
|
157
|
+
graph: {
|
|
158
|
+
nodes: graph.nodes,
|
|
159
|
+
edges: graph.edges
|
|
160
|
+
},
|
|
161
|
+
stale: {
|
|
162
|
+
groups: staleGroups,
|
|
163
|
+
reasons: staleReasons
|
|
164
|
+
},
|
|
165
|
+
warnings,
|
|
166
|
+
selectors: {
|
|
167
|
+
// Manifest of selectors the route honours. Only `selectStaleMetadataGroups`
|
|
168
|
+
// is wired through HTTP (via `?staleKind=&staleId=`). The remaining
|
|
169
|
+
// selectors are exposed as importable helpers for server-side consumers
|
|
170
|
+
// and the read-only inspector; they are NOT toggled through query
|
|
171
|
+
// params in V1.
|
|
172
|
+
httpEnabled: ["selectStaleMetadataGroups"],
|
|
173
|
+
helperOnly: [
|
|
174
|
+
"selectWidgetRequiredFields",
|
|
175
|
+
"selectWorkflowNodeInputSchema",
|
|
176
|
+
"selectObjectFilterableFields",
|
|
177
|
+
"selectObjectSortableFields",
|
|
178
|
+
"selectRunLineage"
|
|
179
|
+
]
|
|
180
|
+
}
|
|
181
|
+
});
|
|
182
|
+
}
|
|
183
|
+
|
|
184
|
+
export { GET };
|
|
@@ -119,8 +119,30 @@ async function POST(request) {
|
|
|
119
119
|
) || null;
|
|
120
120
|
const records = await resolver.fetchRecords(adapterConfig, connection, binding);
|
|
121
121
|
const fetchedAt = new Date().toISOString();
|
|
122
|
-
|
|
123
|
-
|
|
122
|
+
// Persist the records under the object's `id` (canonical sidecar key).
|
|
123
|
+
// We also include `objectId`, `objectSourceId`, and `bindingSourceId`
|
|
124
|
+
// metadata so reader-side hydration in `lib/workspace-data-model.js`
|
|
125
|
+
// can fall back across the three places that may legitimately store
|
|
126
|
+
// the same key (object.id, object.sourceId, object.binding.sourceId)
|
|
127
|
+
// without having to re-parse the object.
|
|
128
|
+
const objectSourceId = typeof obj.sourceId === "string" && obj.sourceId.trim() ? obj.sourceId.trim() : null;
|
|
129
|
+
const bindingSourceId = typeof binding.sourceId === "string" && binding.sourceId.trim() ? binding.sourceId.trim() : null;
|
|
130
|
+
await writeWorkspaceSourceRecords(sourceId, records, {
|
|
131
|
+
integrationId,
|
|
132
|
+
fetchedAt,
|
|
133
|
+
objectId: sourceId,
|
|
134
|
+
objectSourceId,
|
|
135
|
+
bindingSourceId
|
|
136
|
+
});
|
|
137
|
+
refreshed.push({
|
|
138
|
+
sourceId,
|
|
139
|
+
integrationId,
|
|
140
|
+
recordCount: records.length,
|
|
141
|
+
fetchedAt,
|
|
142
|
+
objectId: sourceId,
|
|
143
|
+
objectSourceId,
|
|
144
|
+
bindingSourceId
|
|
145
|
+
});
|
|
124
146
|
} catch (err) {
|
|
125
147
|
skipped.push(sourceId);
|
|
126
148
|
skippedDetail.push({
|
|
@@ -9,9 +9,14 @@ import { buildPortalWorkspace, portalCapabilities } from "@/lib/domain/portal";
|
|
|
9
9
|
import {
|
|
10
10
|
describePersistenceMode,
|
|
11
11
|
readWorkspaceConfig,
|
|
12
|
+
readWorkspaceSourceRecords,
|
|
12
13
|
writeWorkspaceConfig
|
|
13
14
|
} from "@/lib/workspace-config";
|
|
14
15
|
|
|
16
|
+
// Workspace Config Contract V1 — PATCH is permanently restricted to these
|
|
17
|
+
// four fields. Sidecar source records (`workspaceSourceRecords`) are exposed
|
|
18
|
+
// on GET for runtime hydration only; they are deliberately NOT in this set.
|
|
19
|
+
// Sidecar writes flow through POST /api/workspace/refresh-sources.
|
|
15
20
|
const ALLOWED_PATCH_FIELDS = new Set(["dashboards", "widgetTypes", "canvas", "dataModel"]);
|
|
16
21
|
|
|
17
22
|
async function GET() {
|
|
@@ -27,6 +32,14 @@ async function GET() {
|
|
|
27
32
|
integrations: groupIntegrationsByLane(integrations)
|
|
28
33
|
};
|
|
29
34
|
const workspaceConfig = await readWorkspaceConfig();
|
|
35
|
+
// Source records hydrate live-backed Data Model objects at runtime.
|
|
36
|
+
// Missing or unreadable sidecar returns `{}` — never throws.
|
|
37
|
+
let workspaceSourceRecords = {};
|
|
38
|
+
try {
|
|
39
|
+
workspaceSourceRecords = (await readWorkspaceSourceRecords()) || {};
|
|
40
|
+
} catch {
|
|
41
|
+
workspaceSourceRecords = {};
|
|
42
|
+
}
|
|
30
43
|
const persistence = describePersistenceMode();
|
|
31
44
|
return NextResponse.json({
|
|
32
45
|
config,
|
|
@@ -35,6 +48,7 @@ async function GET() {
|
|
|
35
48
|
settings,
|
|
36
49
|
workspace: buildPortalWorkspace({ config, adapters, integrations: settings.integrations }),
|
|
37
50
|
workspaceConfig,
|
|
51
|
+
workspaceSourceRecords,
|
|
38
52
|
workspaceConfigPersistence: persistence
|
|
39
53
|
});
|
|
40
54
|
}
|
|
@@ -0,0 +1,74 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* POST /api/workspace/sandbox-agent-auth/login
|
|
3
|
+
*
|
|
4
|
+
* Spawns the catalog-declared login subcommand for a sandbox row whose
|
|
5
|
+
* adapter is `local-agent-host`. The actual subcommand (e.g.
|
|
6
|
+
* `claude auth login`) is read from `lib/sandbox-agent-host-catalog.js` —
|
|
7
|
+
* this route is host-agnostic.
|
|
8
|
+
*
|
|
9
|
+
* Hosts without a documented login subcommand return 400 with
|
|
10
|
+
* `code: "SANDBOX_AGENT_AUTH_LOGIN_UNSUPPORTED"` so the UI can render the
|
|
11
|
+
* host's `notes` string ("sign in via the host CLI directly").
|
|
12
|
+
*
|
|
13
|
+
* Captures stdout, stderr, login URL (if printed), exit code. Token-shaped
|
|
14
|
+
* output is redacted before crossing the response boundary. Raw tokens are
|
|
15
|
+
* NEVER written to `growthub.config.json`.
|
|
16
|
+
*
|
|
17
|
+
* Request body:
|
|
18
|
+
* { objectId: string, name: string }
|
|
19
|
+
*
|
|
20
|
+
* Response:
|
|
21
|
+
* {
|
|
22
|
+
* ok: boolean,
|
|
23
|
+
* status: "active" | "reachable" | "stale" | "missing" | "unknown",
|
|
24
|
+
* provider: string,
|
|
25
|
+
* label: string,
|
|
26
|
+
* binary: string,
|
|
27
|
+
* cwd: string,
|
|
28
|
+
* exitCode: number | null,
|
|
29
|
+
* timedOut: boolean,
|
|
30
|
+
* durationMs: number,
|
|
31
|
+
* stdout: string,
|
|
32
|
+
* stderr: string,
|
|
33
|
+
* loginUrl: string | null,
|
|
34
|
+
* message: string,
|
|
35
|
+
* checkedAt: string
|
|
36
|
+
* }
|
|
37
|
+
*/
|
|
38
|
+
|
|
39
|
+
import { NextResponse } from "next/server";
|
|
40
|
+
import { runAgentLogin } from "@/lib/sandbox-agent-auth";
|
|
41
|
+
|
|
42
|
+
async function POST(request) {
|
|
43
|
+
let body;
|
|
44
|
+
try {
|
|
45
|
+
body = await request.json();
|
|
46
|
+
} catch {
|
|
47
|
+
return NextResponse.json({ ok: false, error: "invalid JSON body" }, { status: 400 });
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
const objectId = typeof body?.objectId === "string" ? body.objectId.trim() : "";
|
|
51
|
+
const name = typeof body?.name === "string" ? body.name.trim() : "";
|
|
52
|
+
if (!objectId || !name) {
|
|
53
|
+
return NextResponse.json(
|
|
54
|
+
{ ok: false, error: "objectId and name are required" },
|
|
55
|
+
{ status: 400 }
|
|
56
|
+
);
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
try {
|
|
60
|
+
const result = await runAgentLogin({ objectId, name });
|
|
61
|
+
return NextResponse.json(result);
|
|
62
|
+
} catch (error) {
|
|
63
|
+
return NextResponse.json(
|
|
64
|
+
{
|
|
65
|
+
ok: false,
|
|
66
|
+
error: error?.message || "Agent login failed",
|
|
67
|
+
code: error?.code || null
|
|
68
|
+
},
|
|
69
|
+
{ status: error?.code === "SANDBOX_AGENT_AUTH_NOT_FOUND" ? 404 : 400 }
|
|
70
|
+
);
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
export { POST };
|
|
@@ -0,0 +1,67 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* POST /api/workspace/sandbox-agent-auth/logout
|
|
3
|
+
*
|
|
4
|
+
* Spawns the catalog-declared logout subcommand for a sandbox row whose
|
|
5
|
+
* adapter is `local-agent-host`. Host-agnostic; the actual subcommand is
|
|
6
|
+
* read from `lib/sandbox-agent-host-catalog.js`.
|
|
7
|
+
*
|
|
8
|
+
* Hosts without a documented logout subcommand return 400 with
|
|
9
|
+
* `code: "SANDBOX_AGENT_AUTH_LOGOUT_UNSUPPORTED"` so the UI can render the
|
|
10
|
+
* host's `notes` string.
|
|
11
|
+
*
|
|
12
|
+
* Request body:
|
|
13
|
+
* { objectId: string, name: string }
|
|
14
|
+
*
|
|
15
|
+
* Response:
|
|
16
|
+
* {
|
|
17
|
+
* ok: boolean,
|
|
18
|
+
* status: "stale" | "missing" | "unknown",
|
|
19
|
+
* provider: string,
|
|
20
|
+
* label: string,
|
|
21
|
+
* binary: string,
|
|
22
|
+
* cwd: string,
|
|
23
|
+
* exitCode: number | null,
|
|
24
|
+
* durationMs: number,
|
|
25
|
+
* stdout: string,
|
|
26
|
+
* stderr: string,
|
|
27
|
+
* message: string,
|
|
28
|
+
* checkedAt: string
|
|
29
|
+
* }
|
|
30
|
+
*/
|
|
31
|
+
|
|
32
|
+
import { NextResponse } from "next/server";
|
|
33
|
+
import { runAgentLogout } from "@/lib/sandbox-agent-auth";
|
|
34
|
+
|
|
35
|
+
async function POST(request) {
|
|
36
|
+
let body;
|
|
37
|
+
try {
|
|
38
|
+
body = await request.json();
|
|
39
|
+
} catch {
|
|
40
|
+
return NextResponse.json({ ok: false, error: "invalid JSON body" }, { status: 400 });
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
const objectId = typeof body?.objectId === "string" ? body.objectId.trim() : "";
|
|
44
|
+
const name = typeof body?.name === "string" ? body.name.trim() : "";
|
|
45
|
+
if (!objectId || !name) {
|
|
46
|
+
return NextResponse.json(
|
|
47
|
+
{ ok: false, error: "objectId and name are required" },
|
|
48
|
+
{ status: 400 }
|
|
49
|
+
);
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
try {
|
|
53
|
+
const result = await runAgentLogout({ objectId, name });
|
|
54
|
+
return NextResponse.json(result);
|
|
55
|
+
} catch (error) {
|
|
56
|
+
return NextResponse.json(
|
|
57
|
+
{
|
|
58
|
+
ok: false,
|
|
59
|
+
error: error?.message || "Agent logout failed",
|
|
60
|
+
code: error?.code || null
|
|
61
|
+
},
|
|
62
|
+
{ status: error?.code === "SANDBOX_AGENT_AUTH_NOT_FOUND" ? 404 : 400 }
|
|
63
|
+
);
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
export { POST };
|
|
@@ -0,0 +1,77 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* POST /api/workspace/sandbox-agent-auth/status
|
|
3
|
+
*
|
|
4
|
+
* Probes the local agent CLI for a sandbox row whose adapter is
|
|
5
|
+
* `local-agent-host`. The probe is host-agnostic — each host's reachability
|
|
6
|
+
* + auth-status subcommand lives in `lib/sandbox-agent-host-catalog.js`.
|
|
7
|
+
*
|
|
8
|
+
* Status semantics (uniform across every host):
|
|
9
|
+
* - "active" a real auth probe confirmed authentication
|
|
10
|
+
* - "reachable" CLI is callable but auth NOT yet confirmed
|
|
11
|
+
* - "stale" CLI reachable but auth-shaped failure detected
|
|
12
|
+
* - "missing" binary not on PATH
|
|
13
|
+
* - "unknown" indeterminate
|
|
14
|
+
*
|
|
15
|
+
* A `--version` (or equivalent) probe NEVER promotes to "active" — the
|
|
16
|
+
* next sandbox-run is the source of truth for session readiness.
|
|
17
|
+
*
|
|
18
|
+
* Side effect: stamps `agentAuthStatus` + sibling fields onto the row so
|
|
19
|
+
* the Data Model record drawer surfaces the result without a follow-up
|
|
20
|
+
* load.
|
|
21
|
+
*
|
|
22
|
+
* Request body:
|
|
23
|
+
* { objectId: string, name: string }
|
|
24
|
+
*
|
|
25
|
+
* Response (success):
|
|
26
|
+
* {
|
|
27
|
+
* ok: boolean,
|
|
28
|
+
* status: "active" | "reachable" | "stale" | "missing" | "unknown",
|
|
29
|
+
* provider: string,
|
|
30
|
+
* label: string,
|
|
31
|
+
* binary: string,
|
|
32
|
+
* cwd: string,
|
|
33
|
+
* exitCode: number | null,
|
|
34
|
+
* probe: "auth-status" | "version",
|
|
35
|
+
* stdout: string,
|
|
36
|
+
* stderr: string,
|
|
37
|
+
* message: string,
|
|
38
|
+
* checkedAt: string
|
|
39
|
+
* }
|
|
40
|
+
*/
|
|
41
|
+
|
|
42
|
+
import { NextResponse } from "next/server";
|
|
43
|
+
import { checkAgentStatus } from "@/lib/sandbox-agent-auth";
|
|
44
|
+
|
|
45
|
+
async function POST(request) {
|
|
46
|
+
let body;
|
|
47
|
+
try {
|
|
48
|
+
body = await request.json();
|
|
49
|
+
} catch {
|
|
50
|
+
return NextResponse.json({ ok: false, error: "invalid JSON body" }, { status: 400 });
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
const objectId = typeof body?.objectId === "string" ? body.objectId.trim() : "";
|
|
54
|
+
const name = typeof body?.name === "string" ? body.name.trim() : "";
|
|
55
|
+
if (!objectId || !name) {
|
|
56
|
+
return NextResponse.json(
|
|
57
|
+
{ ok: false, error: "objectId and name are required" },
|
|
58
|
+
{ status: 400 }
|
|
59
|
+
);
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
try {
|
|
63
|
+
const result = await checkAgentStatus({ objectId, name });
|
|
64
|
+
return NextResponse.json(result);
|
|
65
|
+
} catch (error) {
|
|
66
|
+
return NextResponse.json(
|
|
67
|
+
{
|
|
68
|
+
ok: false,
|
|
69
|
+
error: error?.message || "Agent auth status check failed",
|
|
70
|
+
code: error?.code || null
|
|
71
|
+
},
|
|
72
|
+
{ status: error?.code === "SANDBOX_AGENT_AUTH_NOT_FOUND" ? 404 : 400 }
|
|
73
|
+
);
|
|
74
|
+
}
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
export { POST };
|
|
@@ -77,6 +77,12 @@ import {
|
|
|
77
77
|
} from "@/lib/adapters/sandboxes";
|
|
78
78
|
import { runOrchestrationGraphIfPresent } from "@/lib/orchestration-graph-runner";
|
|
79
79
|
import { parseOrchestrationGraph } from "@/lib/orchestration-graph";
|
|
80
|
+
import {
|
|
81
|
+
discoverRunInputSchema,
|
|
82
|
+
normalizeRunInputsEnvelope,
|
|
83
|
+
validateRunInputsEnvelope,
|
|
84
|
+
summarizeRunInputs
|
|
85
|
+
} from "@/lib/orchestration-run-inputs";
|
|
80
86
|
|
|
81
87
|
function envKeyCandidates(ref) {
|
|
82
88
|
const token = String(ref || "")
|
|
@@ -347,7 +353,8 @@ function buildRunResponse({
|
|
|
347
353
|
allowList,
|
|
348
354
|
result,
|
|
349
355
|
timeoutMs,
|
|
350
|
-
row
|
|
356
|
+
row,
|
|
357
|
+
runInputs
|
|
351
358
|
}) {
|
|
352
359
|
const base = {
|
|
353
360
|
runId,
|
|
@@ -380,6 +387,21 @@ function buildRunResponse({
|
|
|
380
387
|
executionLane: row.executionLane ? String(row.executionLane) : null
|
|
381
388
|
};
|
|
382
389
|
}
|
|
390
|
+
if (runInputs && typeof runInputs === "object") {
|
|
391
|
+
base.input = runInputs;
|
|
392
|
+
base.runInputs = runInputs;
|
|
393
|
+
base.inputSummary = summarizeRunInputs(runInputs);
|
|
394
|
+
}
|
|
395
|
+
if (result && typeof result === "object" && result.swarm && typeof result.swarm === "object") {
|
|
396
|
+
base.swarm = result.swarm;
|
|
397
|
+
}
|
|
398
|
+
if (result && typeof result === "object" && Array.isArray(result.logTree)) {
|
|
399
|
+
base.logTree = result.logTree;
|
|
400
|
+
}
|
|
401
|
+
base.exports = {
|
|
402
|
+
available: ["download-json", "copy-output", "download-stdout", "download-stderr", "download-log-node"],
|
|
403
|
+
external: []
|
|
404
|
+
};
|
|
383
405
|
return base;
|
|
384
406
|
}
|
|
385
407
|
|
|
@@ -448,6 +470,29 @@ async function POST(request) {
|
|
|
448
470
|
? { ...row, orchestrationGraph: draftGraph, orchestrationConfig: draftGraph }
|
|
449
471
|
: row;
|
|
450
472
|
|
|
473
|
+
const inputSchema = discoverRunInputSchema(rowForRun.orchestrationGraph || rowForRun.orchestrationConfig);
|
|
474
|
+
let normalizedRunInputs = null;
|
|
475
|
+
if (body?.runInputs != null) {
|
|
476
|
+
const validation = validateRunInputsEnvelope(body.runInputs, inputSchema);
|
|
477
|
+
if (validation.error) {
|
|
478
|
+
return NextResponse.json({ ok: false, error: validation.error }, { status: 400 });
|
|
479
|
+
}
|
|
480
|
+
if (inputSchema.requiresInput && validation.missing.length > 0) {
|
|
481
|
+
return NextResponse.json({
|
|
482
|
+
ok: false,
|
|
483
|
+
error: `Missing required run input fields: ${validation.missing.join(", ")}`,
|
|
484
|
+
missingFields: validation.missing
|
|
485
|
+
}, { status: 400 });
|
|
486
|
+
}
|
|
487
|
+
normalizedRunInputs = normalizeRunInputsEnvelope(body.runInputs, inputSchema);
|
|
488
|
+
} else if (inputSchema.requiresInput) {
|
|
489
|
+
return NextResponse.json({
|
|
490
|
+
ok: false,
|
|
491
|
+
error: "runInputs is required for this workflow",
|
|
492
|
+
missingFields: (inputSchema.fields || []).filter((f) => f.required).map((f) => f.id)
|
|
493
|
+
}, { status: 400 });
|
|
494
|
+
}
|
|
495
|
+
|
|
451
496
|
const runLocality = normalizeRunLocality(rowForRun);
|
|
452
497
|
const runtime = KNOWN_SANDBOX_RUNTIMES.includes(rowForRun.runtime) ? rowForRun.runtime : "node";
|
|
453
498
|
let adapterId = (typeof rowForRun.adapter === "string" && rowForRun.adapter.trim()) ? rowForRun.adapter.trim() : DEFAULT_SANDBOX_ADAPTER;
|
|
@@ -515,10 +560,32 @@ async function POST(request) {
|
|
|
515
560
|
|
|
516
561
|
const hasNativeGraph = Boolean(parseOrchestrationGraph(rowForRun.orchestrationGraph || rowForRun.orchestrationConfig));
|
|
517
562
|
if (hasNativeGraph && runLocality !== "serverless") {
|
|
518
|
-
const graphResult = await runOrchestrationGraphIfPresent({
|
|
563
|
+
const graphResult = await runOrchestrationGraphIfPresent({
|
|
564
|
+
workspaceConfig,
|
|
565
|
+
row: rowForRun,
|
|
566
|
+
timeoutMs,
|
|
567
|
+
runInputs: normalizedRunInputs,
|
|
568
|
+
executionContext: {
|
|
569
|
+
runId,
|
|
570
|
+
ranAt,
|
|
571
|
+
runtime,
|
|
572
|
+
agentHost,
|
|
573
|
+
adapterId,
|
|
574
|
+
env,
|
|
575
|
+
envRefSlugs,
|
|
576
|
+
envRefsMissing,
|
|
577
|
+
envRefsResolved,
|
|
578
|
+
networkAllow,
|
|
579
|
+
allowList,
|
|
580
|
+
instructions,
|
|
581
|
+
command,
|
|
582
|
+
timeoutMs,
|
|
583
|
+
sandboxName: rowForRun.Name || name
|
|
584
|
+
}
|
|
585
|
+
});
|
|
519
586
|
if (graphResult !== null) {
|
|
520
587
|
result = graphResult;
|
|
521
|
-
effectiveAdapterId = "orchestration-graph";
|
|
588
|
+
effectiveAdapterId = String(graphResult?.adapterMeta?.adapter || "").trim() || "orchestration-graph";
|
|
522
589
|
}
|
|
523
590
|
}
|
|
524
591
|
|
|
@@ -613,7 +680,8 @@ async function POST(request) {
|
|
|
613
680
|
allowList,
|
|
614
681
|
result,
|
|
615
682
|
timeoutMs,
|
|
616
|
-
row: rowForRun
|
|
683
|
+
row: rowForRun,
|
|
684
|
+
runInputs: normalizedRunInputs
|
|
617
685
|
});
|
|
618
686
|
|
|
619
687
|
const sourceId = sandboxRunSourceId(objectId, row.Name || name);
|