agenttwin 0.3.9 → 0.3.11
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/README.md +9 -3
- package/dist/cli.js +263 -71
- package/dist/dashboard/assets/next/static/{xDiek0iCt0gykeKT7O5aO → 8DWbcYS099IMf1RzFG63k}/_buildManifest.js +1 -1
- package/dist/dashboard/assets/next/static/chunks/1255-b815b974120825e7.js +1 -0
- package/dist/dashboard/assets/next/static/chunks/1848-a6938ad74a8e409f.js +1 -0
- package/dist/dashboard/assets/next/static/chunks/3050-a8f395e3fe36d7b9.js +1 -0
- package/dist/dashboard/assets/next/static/chunks/4055.1056568a41510e92.js +1 -0
- package/dist/dashboard/assets/next/static/chunks/4343.023a18f52318dbad.js +1 -0
- package/dist/dashboard/assets/next/static/chunks/4542.0327d0da1c3ea200.js +1 -0
- package/dist/dashboard/assets/next/static/chunks/4bd1b696-f785427dddbba9fb.js +1 -0
- package/dist/dashboard/assets/next/static/chunks/6751.613adb085c9a5b06.js +1 -0
- package/dist/dashboard/assets/next/static/chunks/6787-6a8909259d0ac26c.js +1 -0
- package/dist/dashboard/assets/next/static/chunks/{772-85561381eedc95da.js → 7772-425e96278b46f6c9.js} +1 -1
- package/dist/dashboard/assets/next/static/chunks/8975.5be81ec8b09e3b45.js +1 -0
- package/dist/dashboard/assets/next/static/chunks/9013.0f2be1cc70029afa.js +10 -0
- package/dist/dashboard/assets/next/static/chunks/app/_not-found/page-43db8aef3fa81958.js +1 -0
- package/dist/dashboard/assets/next/static/chunks/app/account/password/page-c2ce3c796c817ca1.js +1 -0
- package/dist/dashboard/assets/next/static/chunks/app/dashboard/layout-28ee3093f86fc075.js +1 -0
- package/dist/dashboard/assets/next/static/chunks/app/dashboard/page-91a2efe02bd25700.js +1 -0
- package/dist/dashboard/assets/next/static/chunks/app/demo/page-4dc63755e0013e7c.js +1 -0
- package/dist/dashboard/assets/next/static/chunks/app/docs/[[...slug]]/page-db473994608d5c77.js +1 -0
- package/dist/dashboard/assets/next/static/chunks/app/enterprise/page-9a33a4aec43377e0.js +1 -0
- package/dist/dashboard/assets/next/static/chunks/app/layout-dd5d4772c11d4cbb.js +1 -0
- package/dist/dashboard/assets/next/static/chunks/app/login/page-d6d4276397724d29.js +1 -0
- package/dist/dashboard/assets/next/static/chunks/app/onboarding/page-1b066c493c248f29.js +1 -0
- package/dist/dashboard/assets/next/static/chunks/app/page-950c663fd42544c0.js +1 -0
- package/dist/dashboard/assets/next/static/chunks/app/pricing/page-757fd3f59cd253fb.js +1 -0
- package/dist/dashboard/assets/next/static/chunks/app/signup/page-d6d4276397724d29.js +1 -0
- package/dist/dashboard/assets/next/static/chunks/app/sitemap.xml/route-83b9d0f0ad5575d4.js +1 -0
- package/dist/dashboard/assets/next/static/chunks/app/v2/dashboard/layout-a5cf565e017886d3.js +1 -0
- package/dist/dashboard/assets/next/static/chunks/app/v2/dashboard/page-eb63255bc8df6433.js +1 -0
- package/dist/dashboard/assets/next/static/chunks/app/v2/layout-abbd9f3b8811f559.js +1 -0
- package/dist/dashboard/assets/next/static/chunks/app/v2/login/page-4f4a342a4c28f9de.js +1 -0
- package/dist/dashboard/assets/next/static/chunks/app/v2/onboarding/page-f04c0b236abaead3.js +1 -0
- package/dist/dashboard/assets/next/static/chunks/app/v2/page-cf8af0afd0589c71.js +1 -0
- package/dist/dashboard/assets/next/static/chunks/app/v2/signup/page-4f4a342a4c28f9de.js +1 -0
- package/dist/dashboard/assets/next/static/chunks/framework-e54b663a2888b5de.js +1 -0
- package/dist/dashboard/assets/next/static/chunks/main-app-1ec13ceef4a34729.js +1 -0
- package/dist/dashboard/assets/next/static/chunks/main-d1fa84e2f669be1c.js +1 -0
- package/dist/dashboard/assets/next/static/chunks/pages/{_app-7d307437aca18ad4.js → _app-6c8c2371b16a04b8.js} +1 -1
- package/dist/dashboard/assets/next/static/chunks/pages/_error-94812ad32cad7365.js +1 -0
- package/dist/dashboard/assets/next/static/chunks/webpack-60f6502c44ed95af.js +1 -0
- package/dist/dashboard/assets/next/static/css/448fc71b62e29403.css +1 -0
- package/dist/dashboard/index.html +2 -2
- package/package.json +1 -1
- package/dist/dashboard/assets/next/static/chunks/255-09fb0d129bb12f51.js +0 -1
- package/dist/dashboard/assets/next/static/chunks/4bd1b696-c023c6e3521b1417.js +0 -1
- package/dist/dashboard/assets/next/static/chunks/787-2d5f834d2374e355.js +0 -1
- package/dist/dashboard/assets/next/static/chunks/848-000d4eb3e2f5358f.js +0 -1
- package/dist/dashboard/assets/next/static/chunks/app/_not-found/page-95856eb81db7799b.js +0 -1
- package/dist/dashboard/assets/next/static/chunks/app/account/password/page-ff06be670b822cfe.js +0 -1
- package/dist/dashboard/assets/next/static/chunks/app/dashboard/layout-64afdd8a8798e3b6.js +0 -1
- package/dist/dashboard/assets/next/static/chunks/app/dashboard/page-762d3d9f26e6c774.js +0 -1
- package/dist/dashboard/assets/next/static/chunks/app/demo/page-8a71f24127eb1e63.js +0 -1
- package/dist/dashboard/assets/next/static/chunks/app/docs/[[...slug]]/page-4edbabcdd90d97e8.js +0 -1
- package/dist/dashboard/assets/next/static/chunks/app/enterprise/page-018bb6762d3d7df4.js +0 -1
- package/dist/dashboard/assets/next/static/chunks/app/layout-9da6df58ef40be60.js +0 -1
- package/dist/dashboard/assets/next/static/chunks/app/login/page-caa5c579f14ec911.js +0 -1
- package/dist/dashboard/assets/next/static/chunks/app/onboarding/page-39cdf1f2e2ba688f.js +0 -1
- package/dist/dashboard/assets/next/static/chunks/app/page-02aaf1666f32d5c1.js +0 -1
- package/dist/dashboard/assets/next/static/chunks/app/pricing/page-f6fb7e78443d1220.js +0 -1
- package/dist/dashboard/assets/next/static/chunks/app/signup/page-caa5c579f14ec911.js +0 -1
- package/dist/dashboard/assets/next/static/chunks/app/sitemap.xml/route-ef5a216a578eb7a8.js +0 -1
- package/dist/dashboard/assets/next/static/chunks/app/v2/dashboard/layout-2936df21408f38fd.js +0 -1
- package/dist/dashboard/assets/next/static/chunks/app/v2/dashboard/page-d23686e9489aa12d.js +0 -1
- package/dist/dashboard/assets/next/static/chunks/app/v2/layout-dc60420b4d88dd63.js +0 -1
- package/dist/dashboard/assets/next/static/chunks/app/v2/login/page-d3a27a2684498e13.js +0 -1
- package/dist/dashboard/assets/next/static/chunks/app/v2/onboarding/page-b48a6cab88bb7f78.js +0 -1
- package/dist/dashboard/assets/next/static/chunks/app/v2/page-c8aaddcc7111e5e3.js +0 -1
- package/dist/dashboard/assets/next/static/chunks/app/v2/signup/page-d3a27a2684498e13.js +0 -1
- package/dist/dashboard/assets/next/static/chunks/framework-292291387d6b2e39.js +0 -1
- package/dist/dashboard/assets/next/static/chunks/main-aea6e71289384293.js +0 -1
- package/dist/dashboard/assets/next/static/chunks/main-app-def27ec5d496405f.js +0 -1
- package/dist/dashboard/assets/next/static/chunks/pages/_error-cb2a52f75f2162e2.js +0 -1
- package/dist/dashboard/assets/next/static/chunks/webpack-7acd0493fe574225.js +0 -1
- package/dist/dashboard/assets/next/static/css/556c40e9d291ee02.css +0 -1
- /package/dist/dashboard/assets/next/static/{xDiek0iCt0gykeKT7O5aO → 8DWbcYS099IMf1RzFG63k}/_ssgManifest.js +0 -0
package/README.md
CHANGED
|
@@ -14,7 +14,7 @@ npx agenttwin init
|
|
|
14
14
|
|
|
15
15
|
`init` discovers your repository and opens a persistent control menu. Bare `npx agenttwin` opens the same entry point. Select a reviewed world, inspect every service's support, run correctness checks, explore seeded faults, replay a failure, verify a source-only patch, save a regression, import an incident, or connect your account—all from this menu, without remembering flags or separate command names. `npx agenttwin refresh` updates discovery without executing application code. Existing advanced commands remain available to CI and coding agents.
|
|
16
16
|
|
|
17
|
-
Discovery writes the inventory, workflow candidates, `agenttwin.config.json`, `ENVIRONMENT.md`, `REPLICATION.md`, and an offline browser report. A discovered candidate is not an executable specification: the menu accepts a reviewed `agenttwin.world.json` with explicit bundled Node files, synthetic services, invocation and required outcomes. The optional demo is clearly labeled as synthetic. Redirected input and `--yes` only perform discovery; they never start the interactive execution loop. `init --json --dry-run` writes nothing.
|
|
17
|
+
Discovery writes the inventory, workflow candidates, `agenttwin.config.json`, `ENVIRONMENT.md`, `REPLICATION.md`, and an offline browser report. A discovered candidate is not an executable specification: the menu accepts a reviewed `agenttwin.world.json` with explicit bundled Node files, synthetic services, invocation and required outcomes. The optional demo is clearly labeled as synthetic. Redirected input and `--yes` only perform discovery; they never start the interactive execution loop. `init --json --dry-run` writes nothing. Interactive init connects and syncs discovery after the first browser approval; saved connections sync without another approval. Noninteractive init never uploads automatically.
|
|
18
18
|
|
|
19
19
|
Seeded exploration includes an unfaulted control and up to 31 timeout-after-write trials. A seed controls the selected service/path and delay, not OS scheduling or arbitrary application randomness. Each trial uses fresh service state. Captured application files, checks, fault cases and receipts stay under `.agenttwin/explorations/`. Replay uses the captured application; patch verification uses current application files with the captured checks and fault cases. Hash mismatches reject accidental snapshot edits. This is local evidence, not a tamper-proof security boundary; protected CI anchors saved regression checks in a trusted Git baseline. Unexercised fault targets and unsupported provider operations remain incomplete.
|
|
20
20
|
|
|
@@ -138,11 +138,11 @@ Older releases without this updater cannot update themselves. After the new rele
|
|
|
138
138
|
|
|
139
139
|
### Coding session and dashboard discovery
|
|
140
140
|
|
|
141
|
-
Run `npx agenttwin init` inside your repository (Node.js 22.12+). Interactive init
|
|
141
|
+
Run `npx agenttwin init` inside your repository (Node.js 22.12+). Interactive init connects your account, syncs project discovery and opens the guided repository menu. It does not launch another coding agent automatically. `npx agenttwin init --agent opencode` launches OpenCode explicitly with your configured provider and its normal permission controls. Provider authentication and usage belong to your coding-agent account. Other coding agents can use `.agenttwin/AGENT-TASK.md`.
|
|
142
142
|
|
|
143
143
|
The session traces workflows across files, records intents and production failure hypotheses in `.agenttwin/WORKFLOWS.md`, and writes metadata to `.agenttwin/agent-discovery.json`. Static candidates retain their original evidence. Agent candidates remain unverified until the workflow is explicitly prepared and executed. Static discovery preserves distinct exported side-effect entrypoints, including repositories with 100 candidates; it does not prove that every business workflow has been found.
|
|
144
144
|
|
|
145
|
-
Create a workspace at https://agenttwin.si/signup/ or run `npx agenttwin login` from your repository. Run `npx agenttwin init
|
|
145
|
+
Create a workspace at https://agenttwin.si/signup/ or run `npx agenttwin login` from your repository. Run `npx agenttwin init`. On first use, sign in in the browser, compare its prefilled connection code with your terminal, and click Connect terminal. Keep init running: project discovery syncs automatically, and onboarding refreshes to show it. Subsequent interactive init runs use the saved connection without another sharing prompt. Terminal credentials are stored with owner-only permissions in `~/.agenttwin/credentials.json`, outside your repository. You can still supply `AGENTTWIN_TOKEN` for automation. Optional `AGENTTWIN_API_URL` selects your API. The website loads account discovery after you connect in Settings, then refreshes while visible. Sync sends names, operations and relative entrypoint metadata; application source uploads happen separately through reviewed bundles. Avoid sensitive names or identifiers in discovery metadata.
|
|
146
146
|
|
|
147
147
|
To execute a workflow, supply an executable Node entrypoint, reviewed supported dependency mappings, synthetic fixtures and expected observable outcomes. Run `npx agenttwin prepare --file preparation.json`, review the bundle, and run it through your AgentTwin API or E2B account. Browser/desktop automation and arbitrary vendor SDKs require additional runtimes/adapters. The CLI is the default runner; select a hosted runner only when the workflow requires it. No local Docker is used.
|
|
148
148
|
|
|
@@ -154,3 +154,9 @@ A run receipt's **Fix & retest** tab exports an evidence-based task for your cod
|
|
|
154
154
|
The website’s Settings page saves workspace name, default execution limit (1–300 seconds for new templates), automatic refresh, compact lists and reduced motion in this browser. Existing reviewed bundles retain their limits. The account section supports token visibility, connection status, manual workspace refresh and disconnect; tokens are never persisted with preferences.
|
|
155
155
|
|
|
156
156
|
Top-right initials open account actions. The sidebar closes into a full-width layout. See `docs-site/src/dashboard.mdx` for the complete guide. The website/API UI has received additional fixes after npm 0.2.2; the published package remains 0.2.2.
|
|
157
|
+
|
|
158
|
+
### Workflow discovery boundaries
|
|
159
|
+
|
|
160
|
+
Discovery identifies individual request branches in Fetch/Bun-style URL dispatch, Express-style callbacks, queue workers and exported provider operations. It follows direct local function calls and relative named imports, including aliases, and recognizes provider hosts in literal HTTP calls. Each candidate retains its source entrypoint and evidence. Router containers and pure exported helpers are not treated as complete workflows.
|
|
161
|
+
|
|
162
|
+
These are possible static code paths, not executed or verified business workflows. Dynamic dispatch, computed destinations, external packages, re-exports and runtime-only integrations can remain unresolved. Prepare inputs, operation coverage and correctness checks before running a candidate. Refresh discovery and sync it to update an already-connected dashboard.
|
package/dist/cli.js
CHANGED
|
@@ -1876,6 +1876,12 @@ function validVerificationUrl(base, value) {
|
|
|
1876
1876
|
return false;
|
|
1877
1877
|
}
|
|
1878
1878
|
}
|
|
1879
|
+
function pairingUrl(base, verificationUrl, userCode) {
|
|
1880
|
+
if (!validVerificationUrl(base, verificationUrl) || !/^[A-F0-9]{10}$/.test(userCode)) throw new Error("Invalid pairing response.");
|
|
1881
|
+
const url = new URL(verificationUrl);
|
|
1882
|
+
url.hash = new URLSearchParams({ user_code: userCode }).toString();
|
|
1883
|
+
return url.toString();
|
|
1884
|
+
}
|
|
1879
1885
|
async function runLogin(log) {
|
|
1880
1886
|
const base = apiBase(process.env.AGENTTWIN_API_URL || "https://api.agenttwin.si");
|
|
1881
1887
|
const request = async (path2, input = {}) => fetch(base + path2, { method: "POST", headers: { "content-type": "application/json" }, body: JSON.stringify(input), redirect: "error", signal: AbortSignal.timeout(15e3) });
|
|
@@ -1883,12 +1889,13 @@ async function runLogin(log) {
|
|
|
1883
1889
|
if (!response.ok) throw new Error("Cannot start sign in. Please try again.");
|
|
1884
1890
|
const device = await response.json();
|
|
1885
1891
|
if (!/^[a-f0-9]{64}$/.test(device.deviceCode) || !/^[A-F0-9]{10}$/.test(device.userCode) || !validVerificationUrl(base, device.verificationUrl)) throw new Error("Invalid pairing response.");
|
|
1886
|
-
|
|
1892
|
+
const browserUrl = pairingUrl(base, device.verificationUrl, device.userCode);
|
|
1893
|
+
log(`Sign in or create an account at ${browserUrl}`);
|
|
1887
1894
|
log(`Enter this code to connect this terminal: ${device.userCode}`);
|
|
1888
|
-
log("
|
|
1889
|
-
if (process.platform === "darwin") execFile("open", [
|
|
1895
|
+
log("Compare the browser code with this terminal, then choose Connect terminal. No passwords or API keys to paste.");
|
|
1896
|
+
if (process.platform === "darwin") execFile("open", [browserUrl], () => {
|
|
1890
1897
|
});
|
|
1891
|
-
else if (process.platform === "linux") execFile("xdg-open", [
|
|
1898
|
+
else if (process.platform === "linux") execFile("xdg-open", [browserUrl], () => {
|
|
1892
1899
|
});
|
|
1893
1900
|
const deadline = Date.now() + 6e5;
|
|
1894
1901
|
while (Date.now() < deadline) {
|
|
@@ -1899,7 +1906,7 @@ async function runLogin(log) {
|
|
|
1899
1906
|
if (!poll.ok || !/^at_user_[a-f0-9]{64}$/.test(data.token ?? "")) throw new Error(data.error || "Unable to finish sign in.");
|
|
1900
1907
|
await saveConnection(base, data.token);
|
|
1901
1908
|
log(`Connected to ${data.workspace}. Login saved privately outside your repository.`);
|
|
1902
|
-
log("
|
|
1909
|
+
log("Terminal approved. Continuing project connection\u2026");
|
|
1903
1910
|
return;
|
|
1904
1911
|
}
|
|
1905
1912
|
throw new Error("Sign in expired. Run npx agenttwin login again.");
|
|
@@ -1922,7 +1929,7 @@ import { join as join34 } from "path";
|
|
|
1922
1929
|
// package.json
|
|
1923
1930
|
var package_default = {
|
|
1924
1931
|
name: "agenttwin",
|
|
1925
|
-
version: "0.3.
|
|
1932
|
+
version: "0.3.11",
|
|
1926
1933
|
description: "Give your agent a test world before it touches the real one.",
|
|
1927
1934
|
keywords: [
|
|
1928
1935
|
"agent",
|
|
@@ -6713,15 +6720,15 @@ function factoryProviderBindings(source) {
|
|
|
6713
6720
|
constructors.set(specifier.local.name, provider.id);
|
|
6714
6721
|
}
|
|
6715
6722
|
}
|
|
6716
|
-
const
|
|
6723
|
+
const walk2 = (node, visit, skipFunctions = false) => {
|
|
6717
6724
|
if (!node || typeof node !== "object") return;
|
|
6718
6725
|
const value = node;
|
|
6719
6726
|
if (skipFunctions && ["FunctionDeclaration", "FunctionExpression", "ArrowFunctionExpression"].includes(value.type)) return;
|
|
6720
6727
|
visit(value);
|
|
6721
6728
|
for (const [key, child] of Object.entries(value)) {
|
|
6722
6729
|
if (["loc", "start", "end", "comments"].includes(key)) continue;
|
|
6723
|
-
if (Array.isArray(child)) child.forEach((item) =>
|
|
6724
|
-
else if (child && typeof child === "object")
|
|
6730
|
+
if (Array.isArray(child)) child.forEach((item) => walk2(item, visit, skipFunctions));
|
|
6731
|
+
else if (child && typeof child === "object") walk2(child, visit, skipFunctions);
|
|
6725
6732
|
}
|
|
6726
6733
|
};
|
|
6727
6734
|
const factories = /* @__PURE__ */ new Map();
|
|
@@ -6730,7 +6737,7 @@ function factoryProviderBindings(source) {
|
|
|
6730
6737
|
if (node?.type !== "FunctionDeclaration" || !node.id || node.async || node.generator || node.params.length) continue;
|
|
6731
6738
|
const providers = /* @__PURE__ */ new Set();
|
|
6732
6739
|
let unknown = false;
|
|
6733
|
-
|
|
6740
|
+
walk2(node.body, (child) => {
|
|
6734
6741
|
if (child.type !== "ReturnStatement") return;
|
|
6735
6742
|
const argument = child.argument;
|
|
6736
6743
|
if (argument?.type === "NullLiteral") return;
|
|
@@ -6742,18 +6749,18 @@ function factoryProviderBindings(source) {
|
|
|
6742
6749
|
}
|
|
6743
6750
|
const declarations = /* @__PURE__ */ new Map();
|
|
6744
6751
|
const count = (name) => declarations.set(name, (declarations.get(name) ?? 0) + 1);
|
|
6745
|
-
|
|
6752
|
+
walk2(ast.program, (node) => {
|
|
6746
6753
|
if (["FunctionDeclaration", "ClassDeclaration"].includes(node.type) && node.id?.name) count(node.id.name);
|
|
6747
6754
|
if (node.type === "VariableDeclarator" && node.id.type === "Identifier") count(node.id.name);
|
|
6748
6755
|
if (["FunctionDeclaration", "FunctionExpression", "ArrowFunctionExpression"].includes(node.type)) {
|
|
6749
|
-
for (const parameter of node.params ?? [])
|
|
6756
|
+
for (const parameter of node.params ?? []) walk2(parameter, (part) => {
|
|
6750
6757
|
if (part.type === "Identifier") count(part.name);
|
|
6751
6758
|
});
|
|
6752
6759
|
}
|
|
6753
6760
|
});
|
|
6754
6761
|
for (const name of factories.keys()) if ((declarations.get(name) ?? 0) > 1) factories.delete(name);
|
|
6755
6762
|
const ambiguous = /* @__PURE__ */ new Set();
|
|
6756
|
-
|
|
6763
|
+
walk2(ast.program, (node) => {
|
|
6757
6764
|
if (node.type !== "VariableDeclarator" || node.id.type !== "Identifier" || node.init?.type !== "CallExpression" || node.init.callee.type !== "Identifier") return;
|
|
6758
6765
|
const provider = factories.get(node.init.callee.name);
|
|
6759
6766
|
if (!provider) return;
|
|
@@ -6769,6 +6776,195 @@ function factoryProviderBindings(source) {
|
|
|
6769
6776
|
init_entrypoints();
|
|
6770
6777
|
import { createHash as createHash12 } from "crypto";
|
|
6771
6778
|
|
|
6779
|
+
// src/inventory/workflow-graph.ts
|
|
6780
|
+
init_catalog();
|
|
6781
|
+
import { parse as parse3 } from "@babel/parser";
|
|
6782
|
+
import { posix } from "path";
|
|
6783
|
+
function walk(node, visit, skipFunctions = false) {
|
|
6784
|
+
if (!node || typeof node !== "object") return;
|
|
6785
|
+
if (skipFunctions && /^(FunctionDeclaration|FunctionExpression|ArrowFunctionExpression)$/.test(node.type)) return;
|
|
6786
|
+
visit(node);
|
|
6787
|
+
for (const [key, child] of Object.entries(node)) {
|
|
6788
|
+
if (["loc", "start", "end", "comments", "tokens"].includes(key)) continue;
|
|
6789
|
+
if (Array.isArray(child)) child.forEach((item) => walk(item, visit, skipFunctions));
|
|
6790
|
+
else if (child && typeof child === "object") walk(child, visit, skipFunctions);
|
|
6791
|
+
}
|
|
6792
|
+
}
|
|
6793
|
+
function workflowGraph(sources, operations, tools) {
|
|
6794
|
+
const modules = /* @__PURE__ */ new Map();
|
|
6795
|
+
const gaps = [];
|
|
6796
|
+
const resolve19 = (path2, specifier) => {
|
|
6797
|
+
const base = posix.normalize(posix.join(posix.dirname(path2), specifier));
|
|
6798
|
+
return [base, ...[".ts", ".tsx", ".js", ".jsx", ".mjs", ".cjs"].map((ext) => base + ext), base.replace(/\.js$/, ".ts"), ...[".ts", ".js"].map((ext) => base + "/index" + ext)].find((candidate) => sources.has(candidate));
|
|
6799
|
+
};
|
|
6800
|
+
for (const [path2, source] of sources) {
|
|
6801
|
+
if (!/\.[cm]?[jt]sx?$/.test(path2)) continue;
|
|
6802
|
+
let ast;
|
|
6803
|
+
try {
|
|
6804
|
+
ast = parse3(source, { sourceType: "unambiguous", plugins: ["typescript", "jsx"] });
|
|
6805
|
+
} catch {
|
|
6806
|
+
gaps.push(`Workflow syntax could not be parsed: ${path2}`);
|
|
6807
|
+
continue;
|
|
6808
|
+
}
|
|
6809
|
+
const mod = { functions: /* @__PURE__ */ new Map(), imports: /* @__PURE__ */ new Map(), routes: [], ast };
|
|
6810
|
+
modules.set(path2, mod);
|
|
6811
|
+
const unit = (name, node, exported, trigger = false) => ({ path: path2, name, node, exported, trigger, startLine: node.loc.start.line, endLine: node.loc.end.line });
|
|
6812
|
+
for (const item of ast.program.body) {
|
|
6813
|
+
if (item.type === "ImportDeclaration" && item.importKind !== "type" && item.source.value.startsWith(".")) {
|
|
6814
|
+
const target = resolve19(path2, item.source.value);
|
|
6815
|
+
if (target) for (const spec of item.specifiers) {
|
|
6816
|
+
if (spec.type === "ImportSpecifier" && spec.importKind !== "type") mod.imports.set(spec.local.name, { path: target, name: spec.imported.name ?? spec.imported.value });
|
|
6817
|
+
if (spec.type === "ImportDefaultSpecifier") mod.imports.set(spec.local.name, { path: target, name: "default" });
|
|
6818
|
+
}
|
|
6819
|
+
}
|
|
6820
|
+
const exported = item.type === "ExportNamedDeclaration" || item.type === "ExportDefaultDeclaration";
|
|
6821
|
+
const declaration = exported ? item.declaration : item;
|
|
6822
|
+
if (declaration?.type === "FunctionDeclaration") {
|
|
6823
|
+
const name = declaration.id?.name ?? "default";
|
|
6824
|
+
mod.functions.set(name, { ...unit(name, declaration.body, exported), parameters: declaration.params });
|
|
6825
|
+
if (item.type === "ExportDefaultDeclaration") mod.functions.set("default", mod.functions.get(name));
|
|
6826
|
+
}
|
|
6827
|
+
if (declaration?.type === "VariableDeclaration") for (const variable of declaration.declarations) {
|
|
6828
|
+
if (variable.id.type !== "Identifier") continue;
|
|
6829
|
+
const fn = variable.init;
|
|
6830
|
+
if (fn && ["ArrowFunctionExpression", "FunctionExpression"].includes(fn.type)) mod.functions.set(variable.id.name, { ...unit(variable.id.name, fn.body, exported), parameters: fn.params });
|
|
6831
|
+
}
|
|
6832
|
+
}
|
|
6833
|
+
walk(ast.program, (fn) => {
|
|
6834
|
+
if (!["FunctionDeclaration", "FunctionExpression", "ArrowFunctionExpression"].includes(fn.type) || fn.body?.type !== "BlockStatement") return;
|
|
6835
|
+
const paths = /* @__PURE__ */ new Set();
|
|
6836
|
+
const patterns = /* @__PURE__ */ new Map();
|
|
6837
|
+
walk(fn.body, (node) => {
|
|
6838
|
+
if (node.type !== "VariableDeclarator" || node.id.type !== "Identifier") return;
|
|
6839
|
+
if (node.init?.type === "MemberExpression" && node.init.property.name === "pathname") paths.add(node.id.name);
|
|
6840
|
+
const call = node.init;
|
|
6841
|
+
if (call?.type === "CallExpression" && call.callee.type === "MemberExpression" && paths.has(call.callee.object.name) && call.callee.property.name === "match" && call.arguments[0]?.type === "RegExpLiteral") patterns.set(node.id.name, "/" + call.arguments[0].pattern + "/");
|
|
6842
|
+
}, true);
|
|
6843
|
+
if (paths.size === 0) return;
|
|
6844
|
+
walk(fn.body, (node) => {
|
|
6845
|
+
if (node.type !== "IfStatement") return;
|
|
6846
|
+
const routes = /* @__PURE__ */ new Set();
|
|
6847
|
+
const methods = /* @__PURE__ */ new Set();
|
|
6848
|
+
walk(node.test, (test) => {
|
|
6849
|
+
if (test.type === "Identifier" && patterns.has(test.name)) routes.add(patterns.get(test.name));
|
|
6850
|
+
if (test.type !== "BinaryExpression" || !["===", "=="].includes(test.operator)) return;
|
|
6851
|
+
for (const [left, right] of [[test.left, test.right], [test.right, test.left]]) {
|
|
6852
|
+
if (right.type !== "StringLiteral") continue;
|
|
6853
|
+
if (left.type === "Identifier" && paths.has(left.name) && right.value.startsWith("/")) routes.add(right.value);
|
|
6854
|
+
if (left.type === "MemberExpression" && left.property.name === "method" && /^(GET|POST|PUT|PATCH|DELETE|HEAD|OPTIONS)$/.test(right.value)) methods.add(right.value);
|
|
6855
|
+
}
|
|
6856
|
+
});
|
|
6857
|
+
let handlesRequest = false;
|
|
6858
|
+
walk(node.consequent, (part) => {
|
|
6859
|
+
if (part.type === "CallExpression" || part.type === "NewExpression" || part.type === "ReturnStatement" && part.argument && part.argument.type !== "NullLiteral") handlesRequest = true;
|
|
6860
|
+
}, true);
|
|
6861
|
+
if (!handlesRequest) return;
|
|
6862
|
+
for (const route of routes) {
|
|
6863
|
+
const method = methods.size === 1 ? [...methods][0] : "HTTP";
|
|
6864
|
+
mod.routes.push({ ...unit(`route:${method}:${route}@${node.loc.start.line}`, node.consequent, false, true), parameters: fn.params });
|
|
6865
|
+
}
|
|
6866
|
+
}, true);
|
|
6867
|
+
});
|
|
6868
|
+
walk(ast.program, (node) => {
|
|
6869
|
+
if (node.type !== "CallExpression" || node.callee.type !== "Identifier" || node.callee.name !== "fetch") return;
|
|
6870
|
+
const url = node.arguments[0];
|
|
6871
|
+
const value = url?.type === "StringLiteral" ? url.value : url?.type === "TemplateLiteral" ? url.quasis[0]?.value.cooked : void 0;
|
|
6872
|
+
if (!value || !/^https?:\/\//.test(value)) return;
|
|
6873
|
+
let host;
|
|
6874
|
+
try {
|
|
6875
|
+
host = new URL(value).hostname;
|
|
6876
|
+
} catch {
|
|
6877
|
+
return;
|
|
6878
|
+
}
|
|
6879
|
+
const provider = PROVIDERS.find((p) => p.signatures.hosts?.some((h2) => host === h2 || host.endsWith("." + h2)));
|
|
6880
|
+
const toolId = provider && tools.get(provider.id);
|
|
6881
|
+
if (!toolId) return;
|
|
6882
|
+
const methodNode = node.arguments[1]?.properties?.find((p) => p.key?.name === "method" || p.key?.value === "method")?.value;
|
|
6883
|
+
const method = methodNode ? methodNode.type === "StringLiteral" ? methodNode.value.toUpperCase() : "UNKNOWN" : "GET";
|
|
6884
|
+
const line = node.loc.start.line;
|
|
6885
|
+
operations.push({ path: path2, line, toolId, kind: method === "GET" || method === "HEAD" ? "reads" : "writes", label: `HTTP ${method} ${host}`, resource: provider.id, ref: `${path2}:${line}` });
|
|
6886
|
+
});
|
|
6887
|
+
}
|
|
6888
|
+
const adjacency = /* @__PURE__ */ new Map();
|
|
6889
|
+
const resolveCalls = (entry) => {
|
|
6890
|
+
const cached = adjacency.get(entry);
|
|
6891
|
+
if (cached) return cached;
|
|
6892
|
+
const found = /* @__PURE__ */ new Set();
|
|
6893
|
+
const boundNames = (nodes) => {
|
|
6894
|
+
const names = /* @__PURE__ */ new Set();
|
|
6895
|
+
for (const node of nodes) walk(node, (part) => {
|
|
6896
|
+
if (part.type === "Identifier") names.add(part.name);
|
|
6897
|
+
});
|
|
6898
|
+
return names;
|
|
6899
|
+
};
|
|
6900
|
+
const scan = (node, shadowed) => {
|
|
6901
|
+
if (!node || typeof node !== "object") return;
|
|
6902
|
+
if (node.type === "FunctionDeclaration") return;
|
|
6903
|
+
if (["ArrowFunctionExpression", "FunctionExpression"].includes(node.type)) {
|
|
6904
|
+
scan(node.body, /* @__PURE__ */ new Set([...shadowed, ...boundNames(node.params)]));
|
|
6905
|
+
return;
|
|
6906
|
+
}
|
|
6907
|
+
if (node.type === "BlockStatement") {
|
|
6908
|
+
shadowed = new Set(shadowed);
|
|
6909
|
+
for (const statement of node.body) {
|
|
6910
|
+
if (statement.type === "VariableDeclaration") for (const declaration of statement.declarations) for (const name of boundNames([declaration.id])) shadowed.add(name);
|
|
6911
|
+
if (statement.type === "FunctionDeclaration" && statement.id) shadowed.add(statement.id.name);
|
|
6912
|
+
}
|
|
6913
|
+
}
|
|
6914
|
+
if (node.type === "CallExpression" && node.callee.type === "Identifier" && !shadowed.has(node.callee.name)) {
|
|
6915
|
+
const mod = modules.get(entry.path);
|
|
6916
|
+
const local = mod.functions.get(node.callee.name);
|
|
6917
|
+
const imported = mod.imports.get(node.callee.name);
|
|
6918
|
+
const target = local ?? (imported ? modules.get(imported.path)?.functions.get(imported.name) : void 0);
|
|
6919
|
+
if (target && (local || target.exported)) found.add(target);
|
|
6920
|
+
}
|
|
6921
|
+
for (const [key, child] of Object.entries(node)) {
|
|
6922
|
+
if (["loc", "start", "end", "comments", "tokens"].includes(key)) continue;
|
|
6923
|
+
if (Array.isArray(child)) child.forEach((item) => scan(item, shadowed));
|
|
6924
|
+
else if (child && typeof child === "object") scan(child, shadowed);
|
|
6925
|
+
}
|
|
6926
|
+
};
|
|
6927
|
+
scan(entry.node, boundNames(entry.parameters ?? []));
|
|
6928
|
+
const result = [...found];
|
|
6929
|
+
adjacency.set(entry, result);
|
|
6930
|
+
return result;
|
|
6931
|
+
};
|
|
6932
|
+
const byPath = /* @__PURE__ */ new Map();
|
|
6933
|
+
for (const operation of operations) {
|
|
6934
|
+
const list2 = byPath.get(operation.path) ?? [];
|
|
6935
|
+
list2.push(operation);
|
|
6936
|
+
byPath.set(operation.path, list2);
|
|
6937
|
+
}
|
|
6938
|
+
const directEffects = /* @__PURE__ */ new Map();
|
|
6939
|
+
const effects = (unit) => {
|
|
6940
|
+
let result = directEffects.get(unit);
|
|
6941
|
+
if (!result) {
|
|
6942
|
+
result = (byPath.get(unit.path) ?? []).filter((op) => op.line >= unit.startLine && op.line <= unit.endLine);
|
|
6943
|
+
directEffects.set(unit, result);
|
|
6944
|
+
}
|
|
6945
|
+
return result;
|
|
6946
|
+
};
|
|
6947
|
+
const entries = [];
|
|
6948
|
+
for (const mod of modules.values()) {
|
|
6949
|
+
for (const entry of [...new Set(mod.functions.values()), ...mod.routes]) {
|
|
6950
|
+
if (!entry.exported && !entry.trigger) continue;
|
|
6951
|
+
if (!entry.trigger && mod.routes.some((route) => route.startLine >= entry.startLine && route.endLine <= entry.endLine)) continue;
|
|
6952
|
+
const visited = /* @__PURE__ */ new Set();
|
|
6953
|
+
const pending = [entry];
|
|
6954
|
+
while (pending.length) {
|
|
6955
|
+
const current = pending.pop();
|
|
6956
|
+
if (visited.has(current)) continue;
|
|
6957
|
+
visited.add(current);
|
|
6958
|
+
pending.push(...resolveCalls(current));
|
|
6959
|
+
}
|
|
6960
|
+
const calls = [...new Set([...visited].flatMap(effects))];
|
|
6961
|
+
if (!entry.trigger && !calls.some((call) => call.kind === "writes" || call.kind === "emits")) continue;
|
|
6962
|
+
entries.push({ ...entry, calls, evidence: [...visited].map((u) => ({ source: "static", ref: `${u.path}:${u.startLine}` })) });
|
|
6963
|
+
}
|
|
6964
|
+
}
|
|
6965
|
+
return { entries, gaps };
|
|
6966
|
+
}
|
|
6967
|
+
|
|
6772
6968
|
// src/inventory/operations.ts
|
|
6773
6969
|
var WRITE_VERBS = /* @__PURE__ */ new Set([
|
|
6774
6970
|
"create",
|
|
@@ -7187,32 +7383,47 @@ async function buildRelationships(apps, tools, files, decisions) {
|
|
|
7187
7383
|
verified: false
|
|
7188
7384
|
});
|
|
7189
7385
|
}
|
|
7190
|
-
|
|
7386
|
+
const allowedCalls = entrypointCalls.filter((call) => decidedRecords.some((record2) => record2.toolId === call.toolId && record2.kind === call.kind && record2.evidence.some((e) => e.ref === call.ref)));
|
|
7387
|
+
const graph = workflowGraph(sourceText, allowedCalls, new Map(providerTools.map((t) => [t.providerId, t.id])));
|
|
7388
|
+
gaps.push(...graph.gaps);
|
|
7389
|
+
const entries = graph.entries.map((entry) => ({ ...entry, evidence: entry.evidence }));
|
|
7390
|
+
for (const [path2, text] of sourceText) for (const entry of exportedEntrypoints(text)) {
|
|
7391
|
+
if (!/^(route|job):/.test(entry.name)) continue;
|
|
7392
|
+
entries.push({
|
|
7393
|
+
...entry,
|
|
7394
|
+
path: path2,
|
|
7395
|
+
node: {},
|
|
7396
|
+
exported: false,
|
|
7397
|
+
trigger: true,
|
|
7398
|
+
calls: allowedCalls.filter((call) => call.path === path2 && call.line >= entry.startLine && call.line <= entry.endLine),
|
|
7399
|
+
evidence: [{ source: "static", ref: `${path2}:${entry.startLine}` }]
|
|
7400
|
+
});
|
|
7401
|
+
}
|
|
7402
|
+
for (const entry of entries) {
|
|
7403
|
+
const { path: path2 } = entry;
|
|
7191
7404
|
const app = appForPath(apps, path2);
|
|
7192
|
-
|
|
7193
|
-
|
|
7194
|
-
|
|
7195
|
-
|
|
7196
|
-
|
|
7197
|
-
|
|
7198
|
-
|
|
7199
|
-
|
|
7200
|
-
|
|
7201
|
-
|
|
7202
|
-
|
|
7203
|
-
|
|
7204
|
-
|
|
7205
|
-
|
|
7206
|
-
|
|
7207
|
-
|
|
7208
|
-
|
|
7209
|
-
|
|
7210
|
-
|
|
7211
|
-
|
|
7212
|
-
|
|
7213
|
-
|
|
7214
|
-
});
|
|
7215
|
-
}
|
|
7405
|
+
const calls = entry.calls.filter((call) => !rejected.has(opRelId(appForPath(apps, call.path).id, call.toolId, call.kind)));
|
|
7406
|
+
if (!entry.trigger && !calls.some((call) => call.kind === "writes" || call.kind === "emits")) continue;
|
|
7407
|
+
const dependencies = [...new Set(calls.map((call) => call.toolId))];
|
|
7408
|
+
const evidence = [...new Map([...entry.evidence, ...calls.map((call) => ({ source: "static", ref: call.ref }))].map((ref) => [ref.ref, ref])).values()];
|
|
7409
|
+
workflows.push({
|
|
7410
|
+
id: "entry:" + createHash12("sha256").update(path2 + ":" + entry.name).digest("hex").slice(0, 20),
|
|
7411
|
+
title: entry.name,
|
|
7412
|
+
entrypoint: path2 + "#" + entry.name.replace(/\\/g, "%5C"),
|
|
7413
|
+
apps: [app.id],
|
|
7414
|
+
tools: dependencies,
|
|
7415
|
+
steps: dependencies.map((tool2) => ({ app: app.id, tool: tool2, operations: [...new Set(calls.filter((c) => c.toolId === tool2).map((c) => c.label))], evidence })),
|
|
7416
|
+
sharedEntities: [...new Set(calls.map((call) => singularize(call.resource)))],
|
|
7417
|
+
evidence,
|
|
7418
|
+
confidence: "medium",
|
|
7419
|
+
risk: "high",
|
|
7420
|
+
rank: 0,
|
|
7421
|
+
supported: false,
|
|
7422
|
+
hypothesis: true,
|
|
7423
|
+
linkingSignals: [entry.trigger ? "explicit request or background-job trigger" : "exported operation with statically linked provider effects", "local helper call chains are possible paths, not proof of runtime execution; invocation and correctness require review"],
|
|
7424
|
+
scopeUncertain: true,
|
|
7425
|
+
verified: false
|
|
7426
|
+
});
|
|
7216
7427
|
}
|
|
7217
7428
|
if (sourceFiles.length === MAX_FILES3) gaps.push("Source scan is limited to 2500 files; split large repositories into explicit roots. Not every workflow can be proven by static discovery.");
|
|
7218
7429
|
const finalWorkflows = workflows.filter((w) => !rejected.has(w.id)).map(
|
|
@@ -7871,6 +8082,10 @@ function scenariosFor(kind) {
|
|
|
7871
8082
|
return [...new Set(list2)];
|
|
7872
8083
|
}
|
|
7873
8084
|
function assertionsFor(candidate) {
|
|
8085
|
+
if (candidate.entrypoint) return [
|
|
8086
|
+
`define the expected response/output for ${candidate.entrypoint}`,
|
|
8087
|
+
"review required final state and forbidden effects; discovery does not infer business correctness"
|
|
8088
|
+
];
|
|
7874
8089
|
const entity = candidate.sharedEntities[0] ?? "record";
|
|
7875
8090
|
return [
|
|
7876
8091
|
`exactly one ${entity} write is applied (no duplicates)`,
|
|
@@ -7893,7 +8108,7 @@ function buildWorkflowPlans(candidates, _tools) {
|
|
|
7893
8108
|
),
|
|
7894
8109
|
sharedEntities: candidate.sharedEntities,
|
|
7895
8110
|
permissions: permissionsFor(kind, candidate.sharedEntities),
|
|
7896
|
-
asyncEvents: eventsFor(kind, candidate.sharedEntities),
|
|
8111
|
+
asyncEvents: candidate.entrypoint ? [] : eventsFor(kind, candidate.sharedEntities),
|
|
7897
8112
|
failureScenarios: scenariosFor(kind),
|
|
7898
8113
|
outcomeAssertions: assertionsFor(candidate),
|
|
7899
8114
|
unknowns: [
|
|
@@ -9056,7 +9271,7 @@ async function runInit(opts) {
|
|
|
9056
9271
|
if (opts.sync) {
|
|
9057
9272
|
const synced = await runSync(opts.cwd);
|
|
9058
9273
|
if (!opts.json) ok2(`Synced ${synced.workflows} candidates: ${synced.dashboard}`);
|
|
9059
|
-
} else if (!opts.json) info(opts.controller ? "
|
|
9274
|
+
} else if (!opts.json) info(opts.controller ? "Discovery saved locally. Interactive init will now connect your account and sync this project." : "Connect this terminal: npx agenttwin login. Then send reviewed discovery: npx agenttwin sync.");
|
|
9060
9275
|
}
|
|
9061
9276
|
process.exitCode = result.exitCode;
|
|
9062
9277
|
return wrote;
|
|
@@ -9066,21 +9281,6 @@ async function runInit(opts) {
|
|
|
9066
9281
|
import { createInterface as createInterface3 } from "readline/promises";
|
|
9067
9282
|
import { readFile as readFile23, writeFile as writeFile22 } from "fs/promises";
|
|
9068
9283
|
import { join as join27, resolve as resolve14 } from "path";
|
|
9069
|
-
|
|
9070
|
-
// src/runtime/local-workspace.ts
|
|
9071
|
-
async function detectLocalWorkspace(request = fetch) {
|
|
9072
|
-
try {
|
|
9073
|
-
const response = await request("http://localhost:8080/v1/dev/workspace", { redirect: "error", signal: AbortSignal.timeout(700) });
|
|
9074
|
-
if (!response.ok) return null;
|
|
9075
|
-
const data = await response.json();
|
|
9076
|
-
if (data.schema !== "agenttwin/local-workspace-v1" || data.dashboard !== "http://localhost:3000/onboarding/") return null;
|
|
9077
|
-
return { api: "http://localhost:8080", dashboard: data.dashboard };
|
|
9078
|
-
} catch {
|
|
9079
|
-
return null;
|
|
9080
|
-
}
|
|
9081
|
-
}
|
|
9082
|
-
|
|
9083
|
-
// src/commands/control.ts
|
|
9084
9284
|
init_store();
|
|
9085
9285
|
init_contracts();
|
|
9086
9286
|
|
|
@@ -9201,25 +9401,17 @@ async function runControl(opts) {
|
|
|
9201
9401
|
};
|
|
9202
9402
|
const connectAndShare = async () => {
|
|
9203
9403
|
const connection = await savedConnection();
|
|
9404
|
+
const destination = process.env.AGENTTWIN_API_URL || connection?.base || "https://api.agenttwin.si";
|
|
9405
|
+
log(`Connecting this project to ${destination}. Sync sends detected service names, workflow metadata, Git metadata and test evidence; application source and credentials stay local.`);
|
|
9204
9406
|
if (!process.env.AGENTTWIN_TOKEN && (!connection || process.env.AGENTTWIN_API_URL && connection.base !== process.env.AGENTTWIN_API_URL)) await runLogin(log);
|
|
9205
|
-
|
|
9206
|
-
|
|
9207
|
-
const result = await runSync(opts.cwd);
|
|
9208
|
-
log(`Project shared: ${opts.cwd}. Open your workspace and check for your project. ${result.dashboard}`);
|
|
9209
|
-
} else log("Kept on this computer. This repository will not appear in your online workspace yet.");
|
|
9407
|
+
const result = await runSync(opts.cwd);
|
|
9408
|
+
log(`Project connected: ${result.tools} services and ${result.workflows} workflow candidates synced. ${result.dashboard}`);
|
|
9210
9409
|
};
|
|
9211
9410
|
try {
|
|
9212
|
-
|
|
9213
|
-
|
|
9214
|
-
|
|
9215
|
-
|
|
9216
|
-
log("Repository discovered locally. To see it in onboarding, connect this terminal and share its reviewed discovery.");
|
|
9217
|
-
if ((await ask("Connect and share this project now? [y/N]")).toLowerCase() === "y") {
|
|
9218
|
-
try {
|
|
9219
|
-
await connectAndShare();
|
|
9220
|
-
} catch (error) {
|
|
9221
|
-
log(`Connection stopped: ${error instanceof Error ? error.message : String(error)}. Retry with option 9.`);
|
|
9222
|
-
}
|
|
9411
|
+
try {
|
|
9412
|
+
await connectAndShare();
|
|
9413
|
+
} catch (error) {
|
|
9414
|
+
log(`Connection stopped: ${error instanceof Error ? error.message : String(error)}. Local discovery is saved. Retry with option 9.`);
|
|
9223
9415
|
}
|
|
9224
9416
|
while (true) {
|
|
9225
9417
|
log("\nAgentTwin \xB7 your repository control center");
|
|
@@ -1 +1 @@
|
|
|
1
|
-
self.__BUILD_MANIFEST=function(e,r,t,_){return{__rewrites:{afterFiles:[],beforeFiles:[],fallback:[]},__routerFilterStatic:{numItems:16,errorRate:1e-4,numBits:307,numHashes:14,bitArray:[0,0,r,1,1,e,r,r,r,e,r,r,e,r,e,r,r,e,r,r,r,e,r,e,e,r,r,r,e,r,e,e,e,e,e,r,e,e,r,e,e,r,e,e,e,e,e,e,e,e,e,e,e,e,r,e,e,e,r,r,e,e,r,e,r,r,r,r,e,r,r,r,r,r,r,e,e,e,r,r,r,e,r,r,e,e,r,e,e,e,r,e,r,r,e,e,e,e,e,e,e,r,r,e,r,r,e,r,e,r,r,e,r,r,r,e,e,e,e,r,e,r,r,r,e,r,r,r,e,r,e,r,r,e,r,e,e,r,e,r,r,e,r,r,e,r,e,e,r,r,e,e,r,e,r,e,e,r,e,e,r,e,e,r,e,r,e,r,r,r,r,e,e,r,r,e,r,e,r,e,r,r,e,e,e,r,e,r,e,e,r,e,e,r,e,r,e,e,r,e,r,r,e,e,e,e,r,r,r,r,r,r,e,e,e,r,r,r,e,r,e,e,r,e,r,e,e,e,r,r,r,e,e,e,e,r,r,e,e,r,e,r,e,r,e,r,e,r,e,r,e,e,r,e,r,r,e,r,e,r,r,r,e,e,e,r,r,e,e,e,r,e,e,e,e,e,r,e,e,e,e,e,e,e,e,r,r,r,e,r,e,e,r,r,r,e,e,e,r,r,e,e,r,r,r,r,e]},__routerFilterDynamic:{numItems:e,errorRate:1e-4,numBits:20,numHashes:14,bitArray:[r,r,e,e,r,r,e,e,e,e,r,r,e,e,r,e,r,e,e,r]},"/_error":["static/chunks/pages/_error-
|
|
1
|
+
self.__BUILD_MANIFEST=function(e,r,t,_){return{__rewrites:{afterFiles:[],beforeFiles:[],fallback:[]},__routerFilterStatic:{numItems:16,errorRate:1e-4,numBits:307,numHashes:14,bitArray:[0,0,r,1,1,e,r,r,r,e,r,r,e,r,e,r,r,e,r,r,r,e,r,e,e,r,r,r,e,r,e,e,e,e,e,r,e,e,r,e,e,r,e,e,e,e,e,e,e,e,e,e,e,e,r,e,e,e,r,r,e,e,r,e,r,r,r,r,e,r,r,r,r,r,r,e,e,e,r,r,r,e,r,r,e,e,r,e,e,e,r,e,r,r,e,e,e,e,e,e,e,r,r,e,r,r,e,r,e,r,r,e,r,r,r,e,e,e,e,r,e,r,r,r,e,r,r,r,e,r,e,r,r,e,r,e,e,r,e,r,r,e,r,r,e,r,e,e,r,r,e,e,r,e,r,e,e,r,e,e,r,e,e,r,e,r,e,r,r,r,r,e,e,r,r,e,r,e,r,e,r,r,e,e,e,r,e,r,e,e,r,e,e,r,e,r,e,e,r,e,r,r,e,e,e,e,r,r,r,r,r,r,e,e,e,r,r,r,e,r,e,e,r,e,r,e,e,e,r,r,r,e,e,e,e,r,r,e,e,r,e,r,e,r,e,r,e,r,e,r,e,e,r,e,r,r,e,r,e,r,r,r,e,e,e,r,r,e,e,e,r,e,e,e,e,e,r,e,e,e,e,e,e,e,e,r,r,r,e,r,e,e,r,r,r,e,e,e,r,r,e,e,r,r,r,r,e]},__routerFilterDynamic:{numItems:e,errorRate:1e-4,numBits:20,numHashes:14,bitArray:[r,r,e,e,r,r,e,e,e,e,r,r,e,e,r,e,r,e,e,r]},"/_error":["static/chunks/pages/_error-94812ad32cad7365.js"],sortedPages:["/_app","/_error"]}}(1,0,1e-4,14),self.__BUILD_MANIFEST_CB&&self.__BUILD_MANIFEST_CB();
|