veryfront 0.1.758 → 0.1.761
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/esm/cli/commands/init/catalog.d.ts.map +1 -1
- package/esm/cli/commands/init/catalog.js +116 -1
- package/esm/cli/shared/config.d.ts +0 -6
- package/esm/cli/shared/config.d.ts.map +1 -1
- package/esm/cli/shared/config.js +64 -8
- package/esm/cli/templates/integration-loader.d.ts.map +1 -1
- package/esm/cli/templates/integration-loader.js +2 -52
- package/esm/cli/templates/manifest.js +2 -2
- package/esm/deno.d.ts +5 -16
- package/esm/deno.js +6 -17
- package/esm/src/agent/ag-ui/tool-shared.d.ts.map +1 -1
- package/esm/src/agent/ag-ui/tool-shared.js +7 -1
- package/esm/src/agent/child-run/invoke-agent-child-runs.d.ts +0 -1
- package/esm/src/agent/child-run/invoke-agent-child-runs.d.ts.map +1 -1
- package/esm/src/agent/child-run/invoke-agent-child-runs.js +0 -1
- package/esm/src/agent/conversation/bootstrap.d.ts +0 -1
- package/esm/src/agent/conversation/bootstrap.d.ts.map +1 -1
- package/esm/src/agent/conversation/bootstrap.js +0 -1
- package/esm/src/agent/conversation/durable.d.ts +0 -1
- package/esm/src/agent/conversation/durable.d.ts.map +1 -1
- package/esm/src/agent/conversation/durable.js +0 -1
- package/esm/src/agent/factory.d.ts.map +1 -1
- package/esm/src/agent/factory.js +7 -1
- package/esm/src/agent/hosted/chat-preparation.d.ts +1 -1
- package/esm/src/agent/hosted/chat-preparation.d.ts.map +1 -1
- package/esm/src/agent/hosted/chat-preparation.js +17 -3
- package/esm/src/agent/hosted/chat-runtime-contract.d.ts +2 -0
- package/esm/src/agent/hosted/chat-runtime-contract.d.ts.map +1 -1
- package/esm/src/agent/hosted/default-chat-runtime.d.ts +2 -0
- package/esm/src/agent/hosted/default-chat-runtime.d.ts.map +1 -1
- package/esm/src/agent/hosted/default-chat-runtime.js +1 -0
- package/esm/src/agent/hosted/default-project-steering-refresh.js +1 -1
- package/esm/src/agent/hosted/project-steering-adapter.d.ts +7 -0
- package/esm/src/agent/hosted/project-steering-adapter.d.ts.map +1 -1
- package/esm/src/agent/hosted/project-steering-adapter.js +12 -1
- package/esm/src/agent/hosted/veryfront-cloud-agent-service.d.ts +4 -0
- package/esm/src/agent/hosted/veryfront-cloud-agent-service.d.ts.map +1 -1
- package/esm/src/agent/hosted/veryfront-cloud-agent-service.js +14 -12
- package/esm/src/agent/index.d.ts +1 -0
- package/esm/src/agent/index.d.ts.map +1 -1
- package/esm/src/agent/index.js +1 -0
- package/esm/src/agent/memory/index.d.ts +1 -1
- package/esm/src/agent/memory/index.d.ts.map +1 -1
- package/esm/src/agent/memory/index.js +1 -1
- package/esm/src/agent/memory/memory-interface.d.ts +7 -0
- package/esm/src/agent/memory/memory-interface.d.ts.map +1 -1
- package/esm/src/agent/memory/memory.d.ts +25 -0
- package/esm/src/agent/memory/memory.d.ts.map +1 -1
- package/esm/src/agent/memory/memory.js +38 -0
- package/esm/src/agent/project/context.d.ts +2 -0
- package/esm/src/agent/project/context.d.ts.map +1 -1
- package/esm/src/agent/project/context.js +1 -0
- package/esm/src/agent/project/steering-mutation.d.ts.map +1 -1
- package/esm/src/agent/project/steering-mutation.js +7 -1
- package/esm/src/agent/runtime/agent-definition.d.ts +6 -0
- package/esm/src/agent/runtime/agent-definition.d.ts.map +1 -1
- package/esm/src/agent/runtime/agent-definition.js +46 -0
- package/esm/src/agent/runtime/agent-delegation-names.d.ts +5 -0
- package/esm/src/agent/runtime/agent-delegation-names.d.ts.map +1 -0
- package/esm/src/agent/runtime/agent-delegation-names.js +8 -0
- package/esm/src/agent/runtime/agent-delegation.d.ts +32 -0
- package/esm/src/agent/runtime/agent-delegation.d.ts.map +1 -0
- package/esm/src/agent/runtime/agent-delegation.js +57 -0
- package/esm/src/agent/runtime/agent-markdown-adapter.d.ts.map +1 -1
- package/esm/src/agent/runtime/agent-markdown-adapter.js +24 -0
- package/esm/src/agent/runtime/defaults.d.ts +0 -2
- package/esm/src/agent/runtime/defaults.d.ts.map +1 -1
- package/esm/src/agent/runtime/defaults.js +0 -2
- package/esm/src/agent/runtime/index.d.ts +9 -0
- package/esm/src/agent/runtime/index.d.ts.map +1 -1
- package/esm/src/agent/runtime/index.js +45 -20
- package/esm/src/agent/runtime/local-tool.d.ts +6 -0
- package/esm/src/agent/runtime/local-tool.d.ts.map +1 -0
- package/esm/src/agent/runtime/local-tool.js +15 -0
- package/esm/src/agent/runtime/project-skill-catalog.d.ts.map +1 -1
- package/esm/src/agent/runtime/project-skill-catalog.js +67 -0
- package/esm/src/agent/runtime/project-skill-loader.d.ts +9 -0
- package/esm/src/agent/runtime/project-skill-loader.d.ts.map +1 -1
- package/esm/src/agent/runtime/project-skill-loader.js +44 -6
- package/esm/src/agent/runtime/skill-metadata.d.ts +23 -0
- package/esm/src/agent/runtime/skill-metadata.d.ts.map +1 -1
- package/esm/src/agent/runtime/skill-metadata.js +11 -0
- package/esm/src/agent/runtime/tool-helpers.d.ts +3 -1
- package/esm/src/agent/runtime/tool-helpers.d.ts.map +1 -1
- package/esm/src/agent/runtime/tool-helpers.js +45 -11
- package/esm/src/agent/schemas/agent.schema.d.ts +1 -0
- package/esm/src/agent/schemas/agent.schema.d.ts.map +1 -1
- package/esm/src/agent/schemas/agent.schema.js +3 -0
- package/esm/src/agent/service/request-auth-cache.d.ts +0 -1
- package/esm/src/agent/service/request-auth-cache.d.ts.map +1 -1
- package/esm/src/agent/service/request-auth-cache.js +0 -1
- package/esm/src/agent/types.d.ts +8 -0
- package/esm/src/agent/types.d.ts.map +1 -1
- package/esm/src/channels/control-plane.d.ts +1 -0
- package/esm/src/channels/control-plane.d.ts.map +1 -1
- package/esm/src/channels/control-plane.js +4 -2
- package/esm/src/chat/ag-ui.d.ts.map +1 -1
- package/esm/src/chat/conversation.d.ts.map +1 -1
- package/esm/src/chat/final-step-fallback.d.ts.map +1 -1
- package/esm/src/chat/protocol.d.ts.map +1 -1
- package/esm/src/chat/protocol.js +1 -0
- package/esm/src/chat/provider-errors.d.ts.map +1 -1
- package/esm/src/chat/provider-errors.js +0 -1
- package/esm/src/chat/stream-watchdog.d.ts +0 -1
- package/esm/src/chat/stream-watchdog.d.ts.map +1 -1
- package/esm/src/chat/stream-watchdog.js +0 -1
- package/esm/src/config/environment-config.d.ts +1 -0
- package/esm/src/config/environment-config.d.ts.map +1 -1
- package/esm/src/config/environment-config.js +4 -0
- package/esm/src/discovery/agent-scoped-capabilities.d.ts +54 -0
- package/esm/src/discovery/agent-scoped-capabilities.d.ts.map +1 -0
- package/esm/src/discovery/agent-scoped-capabilities.js +228 -0
- package/esm/src/discovery/discovery-engine.d.ts.map +1 -1
- package/esm/src/discovery/discovery-engine.js +19 -15
- package/esm/src/discovery/file-discovery.d.ts +10 -0
- package/esm/src/discovery/file-discovery.d.ts.map +1 -1
- package/esm/src/discovery/file-discovery.js +46 -0
- package/esm/src/discovery/handlers/runtime-agent-markdown-handler.d.ts +12 -0
- package/esm/src/discovery/handlers/runtime-agent-markdown-handler.d.ts.map +1 -1
- package/esm/src/discovery/handlers/runtime-agent-markdown-handler.js +99 -22
- package/esm/src/errors/error-registry/agent.d.ts +2 -0
- package/esm/src/errors/error-registry/agent.d.ts.map +1 -1
- package/esm/src/errors/error-registry/agent.js +8 -0
- package/esm/src/errors/error-registry.d.ts +1 -0
- package/esm/src/errors/error-registry.d.ts.map +1 -1
- package/esm/src/extensions/errors.d.ts +2 -0
- package/esm/src/extensions/errors.d.ts.map +1 -1
- package/esm/src/extensions/errors.js +8 -0
- package/esm/src/extensions/loader.d.ts +9 -6
- package/esm/src/extensions/loader.d.ts.map +1 -1
- package/esm/src/extensions/loader.js +39 -4
- package/esm/src/extensions/orchestrate.d.ts +3 -0
- package/esm/src/extensions/orchestrate.d.ts.map +1 -1
- package/esm/src/extensions/orchestrate.js +3 -1
- package/esm/src/integrations/_data.d.ts.map +1 -1
- package/esm/src/integrations/_data.js +31348 -7875
- package/esm/src/integrations/_tool_summaries.d.ts.map +1 -1
- package/esm/src/integrations/_tool_summaries.js +1737 -11
- package/esm/src/integrations/feature-flags.d.ts +1 -1
- package/esm/src/integrations/feature-flags.d.ts.map +1 -1
- package/esm/src/integrations/feature-flags.js +83 -0
- package/esm/src/integrations/schema.d.ts +129 -4
- package/esm/src/integrations/schema.d.ts.map +1 -1
- package/esm/src/integrations/schema.js +126 -1
- package/esm/src/internal-agents/run-stream.d.ts +4 -0
- package/esm/src/internal-agents/run-stream.d.ts.map +1 -1
- package/esm/src/internal-agents/run-stream.js +18 -5
- package/esm/src/mcp/server.d.ts.map +1 -1
- package/esm/src/mcp/server.js +5 -0
- package/esm/src/platform/adapters/base.d.ts +7 -0
- package/esm/src/platform/adapters/base.d.ts.map +1 -1
- package/esm/src/platform/adapters/runtime/deno/adapter.d.ts.map +1 -1
- package/esm/src/platform/adapters/runtime/deno/adapter.js +7 -2
- package/esm/src/registry/project-scoped-registry-manager.d.ts +7 -0
- package/esm/src/registry/project-scoped-registry-manager.d.ts.map +1 -1
- package/esm/src/registry/project-scoped-registry-manager.js +10 -0
- package/esm/src/registry/scoped-registry-facade.d.ts +2 -0
- package/esm/src/registry/scoped-registry-facade.d.ts.map +1 -1
- package/esm/src/registry/scoped-registry-facade.js +4 -0
- package/esm/src/rendering/layouts/utils/component-loader.d.ts +1 -1
- package/esm/src/rendering/layouts/utils/component-loader.d.ts.map +1 -1
- package/esm/src/rendering/layouts/utils/component-loader.js +82 -3
- package/esm/src/rendering/orchestrator/css-cache.d.ts +3 -0
- package/esm/src/rendering/orchestrator/css-cache.d.ts.map +1 -1
- package/esm/src/rendering/orchestrator/css-cache.js +16 -8
- package/esm/src/rendering/orchestrator/mdx.d.ts.map +1 -1
- package/esm/src/rendering/orchestrator/mdx.js +12 -1
- package/esm/src/server/bootstrap.d.ts.map +1 -1
- package/esm/src/server/bootstrap.js +3 -0
- package/esm/src/server/handlers/dev/dashboard/api.js +6 -3
- package/esm/src/server/handlers/request/agent-stream.handler.d.ts.map +1 -1
- package/esm/src/server/handlers/request/agent-stream.handler.js +10 -0
- package/esm/src/server/handlers/request/channel-invoke.handler.d.ts.map +1 -1
- package/esm/src/server/handlers/request/channel-invoke.handler.js +8 -0
- package/esm/src/server/handlers/request/runtime-shutdown-response.d.ts +13 -0
- package/esm/src/server/handlers/request/runtime-shutdown-response.d.ts.map +1 -0
- package/esm/src/server/handlers/request/runtime-shutdown-response.js +16 -0
- package/esm/src/server/production-server.d.ts.map +1 -1
- package/esm/src/server/production-server.js +7 -1
- package/esm/src/server/services/rsc/endpoints/rsc-bundles.generated.d.ts.map +1 -1
- package/esm/src/server/services/rsc/endpoints/rsc-bundles.generated.js +2 -2
- package/esm/src/server/shutdown-state.d.ts +16 -0
- package/esm/src/server/shutdown-state.d.ts.map +1 -0
- package/esm/src/server/shutdown-state.js +22 -0
- package/esm/src/skill/index.d.ts +1 -1
- package/esm/src/skill/index.d.ts.map +1 -1
- package/esm/src/skill/index.js +1 -1
- package/esm/src/skill/registry.d.ts +29 -3
- package/esm/src/skill/registry.d.ts.map +1 -1
- package/esm/src/skill/registry.js +57 -8
- package/esm/src/skill/tools.d.ts.map +1 -1
- package/esm/src/skill/tools.js +27 -25
- package/esm/src/skill/types.d.ts +8 -0
- package/esm/src/skill/types.d.ts.map +1 -1
- package/esm/src/tool/executor.d.ts +7 -1
- package/esm/src/tool/executor.d.ts.map +1 -1
- package/esm/src/tool/executor.js +12 -1
- package/esm/src/tool/index.d.ts +1 -1
- package/esm/src/tool/index.d.ts.map +1 -1
- package/esm/src/tool/index.js +1 -1
- package/esm/src/tool/registry.d.ts +1 -0
- package/esm/src/tool/registry.d.ts.map +1 -1
- package/esm/src/tool/registry.js +25 -0
- package/esm/src/tool/types.d.ts +9 -0
- package/esm/src/tool/types.d.ts.map +1 -1
- package/esm/src/utils/constants/cache.d.ts +8 -0
- package/esm/src/utils/constants/cache.d.ts.map +1 -1
- package/esm/src/utils/constants/cache.js +8 -0
- package/esm/src/utils/constants/index.d.ts +1 -1
- package/esm/src/utils/constants/index.d.ts.map +1 -1
- package/esm/src/utils/constants/index.js +1 -1
- package/esm/src/utils/index.d.ts +1 -1
- package/esm/src/utils/index.d.ts.map +1 -1
- package/esm/src/utils/index.js +1 -1
- package/esm/src/utils/lru-wrapper.d.ts +2 -0
- package/esm/src/utils/lru-wrapper.d.ts.map +1 -1
- package/esm/src/utils/lru-wrapper.js +1 -0
- package/esm/src/utils/version-constant.d.ts +1 -1
- package/esm/src/utils/version-constant.js +1 -1
- package/package.json +1 -45
- package/esm/src/chat/compat.d.ts +0 -782
- package/esm/src/chat/compat.d.ts.map +0 -1
- package/esm/src/chat/compat.js +0 -113
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"catalog.d.ts","sourceRoot":"","sources":["../../../../src/cli/commands/init/catalog.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAEH,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,0BAA0B,CAAC;AAChE,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,YAAY,CAAC;AAC/C,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,gCAAgC,CAAC;AAUnE,MAAM,WAAW,cAAc;IAC7B,EAAE,EAAE,YAAY,CAAC;IACjB,KAAK,EAAE,MAAM,CAAC;IACd,WAAW,EAAE,MAAM,CAAC;CACrB;AAED,eAAO,MAAM,SAAS,EAAE,SAAS,cAAc,EAoBrC,CAAC;AAEX,0DAA0D;AAC1D,wBAAgB,wBAAwB,IAAI,YAAY,EAAE,CAMzD;AAMD,MAAM,WAAW,iBAAiB;IAChC,EAAE,EAAE,eAAe,CAAC;IACpB,KAAK,EAAE,MAAM,CAAC;IACd,WAAW,EAAE,MAAM,CAAC;CACrB;AAED,MAAM,WAAW,mBAAmB;IAClC,IAAI,EAAE,MAAM,CAAC;IACb,YAAY,EAAE,SAAS,iBAAiB,EAAE,CAAC;CAC5C;AAED,eAAO,MAAM,sBAAsB,EAAE,SAAS,mBAAmB,
|
|
1
|
+
{"version":3,"file":"catalog.d.ts","sourceRoot":"","sources":["../../../../src/cli/commands/init/catalog.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAEH,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,0BAA0B,CAAC;AAChE,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,YAAY,CAAC;AAC/C,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,gCAAgC,CAAC;AAUnE,MAAM,WAAW,cAAc;IAC7B,EAAE,EAAE,YAAY,CAAC;IACjB,KAAK,EAAE,MAAM,CAAC;IACd,WAAW,EAAE,MAAM,CAAC;CACrB;AAED,eAAO,MAAM,SAAS,EAAE,SAAS,cAAc,EAoBrC,CAAC;AAEX,0DAA0D;AAC1D,wBAAgB,wBAAwB,IAAI,YAAY,EAAE,CAMzD;AAMD,MAAM,WAAW,iBAAiB;IAChC,EAAE,EAAE,eAAe,CAAC;IACpB,KAAK,EAAE,MAAM,CAAC;IACd,WAAW,EAAE,MAAM,CAAC;CACrB;AAED,MAAM,WAAW,mBAAmB;IAClC,IAAI,EAAE,MAAM,CAAC;IACb,YAAY,EAAE,SAAS,iBAAiB,EAAE,CAAC;CAC5C;AAED,eAAO,MAAM,sBAAsB,EAAE,SAAS,mBAAmB,EAuNvD,CAAC;AAEX,2CAA2C;AAC3C,wBAAgB,kBAAkB,IAAI,iBAAiB,EAAE,CAIxD;AAED,6DAA6D;AAC7D,wBAAgB,2BAA2B,IAAI,YAAY,EAAE,CAM5D;AAED,4DAA4D;AAC5D,wBAAgB,sBAAsB,IAAI,iBAAiB,EAAE,CAa5D;AAED,+DAA+D;AAC/D,wBAAgB,sCAAsC,IAAI,KAAK,CAC7D,YAAY,GAAG;IAAE,QAAQ,CAAC,EAAE,OAAO,CAAA;CAAE,CACtC,CA0BA"}
|
|
@@ -42,6 +42,8 @@ export const INTEGRATION_CATEGORIES = [
|
|
|
42
42
|
{ id: "teams", label: "Teams", description: "Chat, meetings" },
|
|
43
43
|
{ id: "webex", label: "Webex", description: "Messaging, meetings" },
|
|
44
44
|
{ id: "zoom", label: "Zoom", description: "Meetings, webinars" },
|
|
45
|
+
{ id: "google-chat", label: "Google Chat", description: "Spaces, messages" },
|
|
46
|
+
{ id: "dialpad", label: "Dialpad", description: "Calls, SMS" },
|
|
45
47
|
{ id: "twilio", label: "Twilio", description: "SMS, voice" },
|
|
46
48
|
],
|
|
47
49
|
},
|
|
@@ -56,6 +58,11 @@ export const INTEGRATION_CATEGORIES = [
|
|
|
56
58
|
{ id: "trello", label: "Trello", description: "Boards, lists, cards" },
|
|
57
59
|
{ id: "monday", label: "Monday", description: "Work management" },
|
|
58
60
|
{ id: "clickup", label: "ClickUp", description: "Tasks, docs" },
|
|
61
|
+
{ id: "todoist", label: "Todoist", description: "Tasks, projects" },
|
|
62
|
+
{ id: "coda", label: "Coda", description: "Docs, tables" },
|
|
63
|
+
{ id: "basecamp", label: "Basecamp", description: "Projects, to-dos" },
|
|
64
|
+
{ id: "shortcut", label: "Shortcut", description: "Stories, epics" },
|
|
65
|
+
{ id: "productboard", label: "Productboard", description: "Product feedback, roadmaps" },
|
|
59
66
|
{ id: "confluence", label: "Confluence", description: "Wiki pages, spaces" },
|
|
60
67
|
],
|
|
61
68
|
},
|
|
@@ -67,6 +74,10 @@ export const INTEGRATION_CATEGORIES = [
|
|
|
67
74
|
{ id: "bitbucket", label: "Bitbucket", description: "Repos, pull requests" },
|
|
68
75
|
{ id: "sentry", label: "Sentry", description: "Error tracking" },
|
|
69
76
|
{ id: "posthog", label: "PostHog", description: "Product analytics" },
|
|
77
|
+
{ id: "circleci", label: "CircleCI", description: "CI/CD pipelines" },
|
|
78
|
+
{ id: "buildkite", label: "Buildkite", description: "CI/CD pipelines" },
|
|
79
|
+
{ id: "snyk", label: "Snyk", description: "Vulnerability scanning" },
|
|
80
|
+
{ id: "launchdarkly", label: "LaunchDarkly", description: "Feature flags" },
|
|
70
81
|
{ id: "mixpanel", label: "Mixpanel", description: "Analytics, events" },
|
|
71
82
|
],
|
|
72
83
|
},
|
|
@@ -88,6 +99,18 @@ export const INTEGRATION_CATEGORIES = [
|
|
|
88
99
|
{ id: "supabase", label: "Supabase", description: "Postgres, auth, storage" },
|
|
89
100
|
{ id: "neon", label: "Neon", description: "Serverless Postgres" },
|
|
90
101
|
{ id: "snowflake", label: "Snowflake", description: "Data warehouse" },
|
|
102
|
+
{ id: "vercel", label: "Vercel", description: "Deployments, projects" },
|
|
103
|
+
{ id: "netlify", label: "Netlify", description: "Sites, deploys" },
|
|
104
|
+
{ id: "railway", label: "Railway", description: "Projects, deployments" },
|
|
105
|
+
{ id: "render", label: "Render", description: "Services, deploys" },
|
|
106
|
+
{ id: "fly-io", label: "Fly.io", description: "Apps, machines" },
|
|
107
|
+
{ id: "heroku", label: "Heroku", description: "Apps, dynos" },
|
|
108
|
+
{ id: "digitalocean", label: "DigitalOcean", description: "Droplets, databases" },
|
|
109
|
+
{ id: "cloudflare", label: "Cloudflare", description: "Zones, DNS" },
|
|
110
|
+
{ id: "mongodb-atlas", label: "MongoDB Atlas", description: "Clusters, databases" },
|
|
111
|
+
{ id: "planetscale", label: "PlanetScale", description: "MySQL branches" },
|
|
112
|
+
{ id: "clickhouse", label: "ClickHouse", description: "Cloud analytics DB" },
|
|
113
|
+
{ id: "redis-cloud", label: "Redis Cloud", description: "Managed Redis" },
|
|
91
114
|
{ id: "aws", label: "AWS", description: "S3, Lambda, DynamoDB" },
|
|
92
115
|
],
|
|
93
116
|
},
|
|
@@ -96,6 +119,12 @@ export const INTEGRATION_CATEGORIES = [
|
|
|
96
119
|
integrations: [
|
|
97
120
|
{ id: "hubspot", label: "HubSpot", description: "Forms, contacts, and leads" },
|
|
98
121
|
{ id: "salesforce", label: "Salesforce", description: "CRM, sales automation" },
|
|
122
|
+
{ id: "attio", label: "Attio", description: "Modern CRM" },
|
|
123
|
+
{ id: "close", label: "Close", description: "Sales CRM, calls" },
|
|
124
|
+
{ id: "apollo", label: "Apollo.io", description: "Prospecting, enrichment" },
|
|
125
|
+
{ id: "activecampaign", label: "ActiveCampaign", description: "CRM, automation" },
|
|
126
|
+
{ id: "folk", label: "folk", description: "Lightweight CRM" },
|
|
127
|
+
{ id: "salesflare", label: "Salesflare", description: "CRM for small teams" },
|
|
99
128
|
{ id: "pipedrive", label: "Pipedrive", description: "Sales pipeline" },
|
|
100
129
|
],
|
|
101
130
|
},
|
|
@@ -112,6 +141,9 @@ export const INTEGRATION_CATEGORIES = [
|
|
|
112
141
|
name: "Finance",
|
|
113
142
|
integrations: [
|
|
114
143
|
{ id: "stripe", label: "Stripe", description: "Payments, subscriptions" },
|
|
144
|
+
{ id: "paypal", label: "PayPal", description: "Transactions, invoices" },
|
|
145
|
+
{ id: "square", label: "Square", description: "Payments, orders" },
|
|
146
|
+
{ id: "razorpay", label: "Razorpay", description: "Payments (India)" },
|
|
115
147
|
{ id: "quickbooks", label: "QuickBooks", description: "Accounting" },
|
|
116
148
|
{ id: "xero", label: "Xero", description: "Accounting" },
|
|
117
149
|
],
|
|
@@ -121,6 +153,12 @@ export const INTEGRATION_CATEGORIES = [
|
|
|
121
153
|
integrations: [
|
|
122
154
|
{ id: "mailchimp", label: "Mailchimp", description: "Email marketing" },
|
|
123
155
|
{ id: "shopify", label: "Shopify", description: "E-commerce" },
|
|
156
|
+
{ id: "klaviyo", label: "Klaviyo", description: "Email & SMS marketing" },
|
|
157
|
+
{ id: "sendgrid", label: "SendGrid", description: "Transactional email" },
|
|
158
|
+
{ id: "brevo", label: "Brevo", description: "Email campaigns, contacts" },
|
|
159
|
+
{ id: "resend", label: "Resend", description: "Developer email" },
|
|
160
|
+
{ id: "typeform", label: "Typeform", description: "Forms, responses" },
|
|
161
|
+
{ id: "jotform", label: "Jotform", description: "Forms, submissions" },
|
|
124
162
|
{ id: "twitter", label: "Twitter/X", description: "Social media" },
|
|
125
163
|
],
|
|
126
164
|
},
|
|
@@ -130,7 +168,84 @@ export const INTEGRATION_CATEGORIES = [
|
|
|
130
168
|
},
|
|
131
169
|
{
|
|
132
170
|
name: "AI Providers",
|
|
133
|
-
integrations: [
|
|
171
|
+
integrations: [
|
|
172
|
+
{ id: "anthropic", label: "Anthropic", description: "Claude models" },
|
|
173
|
+
{ id: "openai", label: "OpenAI", description: "GPT models, embeddings, images" },
|
|
174
|
+
{ id: "gemini", label: "Gemini", description: "Google Gemini models" },
|
|
175
|
+
{ id: "mistral", label: "Mistral", description: "Mistral models" },
|
|
176
|
+
{ id: "perplexity", label: "Perplexity", description: "Search-grounded answers" },
|
|
177
|
+
{ id: "cohere", label: "Cohere", description: "Chat, embeddings, rerank" },
|
|
178
|
+
{ id: "groq", label: "Groq", description: "Fast LLM inference" },
|
|
179
|
+
{ id: "together-ai", label: "Together AI", description: "Open-model inference" },
|
|
180
|
+
{ id: "fireworks-ai", label: "Fireworks AI", description: "Open-model inference" },
|
|
181
|
+
{ id: "openrouter", label: "OpenRouter", description: "Multi-provider LLM routing" },
|
|
182
|
+
{ id: "replicate", label: "Replicate", description: "Hosted model predictions" },
|
|
183
|
+
{ id: "huggingface", label: "Hugging Face", description: "Hub models, inference" },
|
|
184
|
+
{ id: "elevenlabs", label: "ElevenLabs", description: "Voice synthesis" },
|
|
185
|
+
{ id: "deepgram", label: "Deepgram", description: "Speech-to-text" },
|
|
186
|
+
{ id: "assemblyai", label: "AssemblyAI", description: "Speech-to-text" },
|
|
187
|
+
{ id: "stability-ai", label: "Stability AI", description: "Image generation" },
|
|
188
|
+
{ id: "fal", label: "fal", description: "Generative media inference" },
|
|
189
|
+
],
|
|
190
|
+
},
|
|
191
|
+
{
|
|
192
|
+
name: "AI Infrastructure",
|
|
193
|
+
integrations: [
|
|
194
|
+
{ id: "langsmith", label: "LangSmith", description: "LLM tracing, datasets" },
|
|
195
|
+
{ id: "langfuse", label: "Langfuse", description: "LLM observability" },
|
|
196
|
+
{ id: "pinecone", label: "Pinecone", description: "Vector database" },
|
|
197
|
+
{ id: "qdrant", label: "Qdrant", description: "Vector database" },
|
|
198
|
+
{ id: "weaviate", label: "Weaviate", description: "Vector database" },
|
|
199
|
+
{ id: "browserbase", label: "Browserbase", description: "Headless browsers" },
|
|
200
|
+
{ id: "apify", label: "Apify", description: "Web scraping actors" },
|
|
201
|
+
],
|
|
202
|
+
},
|
|
203
|
+
{
|
|
204
|
+
name: "Search & Web Data",
|
|
205
|
+
integrations: [
|
|
206
|
+
{ id: "algolia", label: "Algolia", description: "Search indexes" },
|
|
207
|
+
{ id: "exa", label: "Exa", description: "Neural web search" },
|
|
208
|
+
{ id: "tavily", label: "Tavily", description: "Search for agents" },
|
|
209
|
+
{ id: "firecrawl", label: "Firecrawl", description: "Scrape & crawl" },
|
|
210
|
+
],
|
|
211
|
+
},
|
|
212
|
+
{
|
|
213
|
+
name: "Observability",
|
|
214
|
+
integrations: [
|
|
215
|
+
{ id: "datadog", label: "Datadog", description: "Monitors, metrics, logs" },
|
|
216
|
+
{ id: "pagerduty", label: "PagerDuty", description: "Incidents, on-call" },
|
|
217
|
+
{ id: "grafana-cloud", label: "Grafana Cloud", description: "Dashboards" },
|
|
218
|
+
{ id: "new-relic", label: "New Relic", description: "NRQL, entities" },
|
|
219
|
+
{ id: "axiom", label: "Axiom", description: "Log analytics" },
|
|
220
|
+
{ id: "betterstack", label: "Better Stack", description: "Uptime monitoring" },
|
|
221
|
+
{ id: "checkly", label: "Checkly", description: "Synthetic monitoring" },
|
|
222
|
+
],
|
|
223
|
+
},
|
|
224
|
+
{
|
|
225
|
+
name: "Data & Analytics",
|
|
226
|
+
integrations: [
|
|
227
|
+
{ id: "google-analytics", label: "Google Analytics", description: "GA4 reports" },
|
|
228
|
+
{ id: "amplitude", label: "Amplitude", description: "Product analytics" },
|
|
229
|
+
{ id: "segment", label: "Segment", description: "Customer data platform" },
|
|
230
|
+
{ id: "metabase", label: "Metabase", description: "BI queries, dashboards" },
|
|
231
|
+
],
|
|
232
|
+
},
|
|
233
|
+
{
|
|
234
|
+
name: "Scheduling & Meetings",
|
|
235
|
+
integrations: [
|
|
236
|
+
{ id: "calendly", label: "Calendly", description: "Scheduling links, events" },
|
|
237
|
+
{ id: "fireflies", label: "Fireflies", description: "Meeting transcripts" },
|
|
238
|
+
{ id: "fathom", label: "Fathom", description: "Meeting summaries" },
|
|
239
|
+
{ id: "gong", label: "Gong", description: "Call intelligence" },
|
|
240
|
+
],
|
|
241
|
+
},
|
|
242
|
+
{
|
|
243
|
+
name: "HR & Recruiting",
|
|
244
|
+
integrations: [
|
|
245
|
+
{ id: "gusto", label: "Gusto", description: "Payroll, employees" },
|
|
246
|
+
{ id: "ashby", label: "Ashby", description: "ATS, candidates" },
|
|
247
|
+
{ id: "lever", label: "Lever", description: "ATS, opportunities" },
|
|
248
|
+
],
|
|
134
249
|
},
|
|
135
250
|
];
|
|
136
251
|
/** Get all integrations as a flat array */
|
|
@@ -1,9 +1,3 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Shared CLI configuration for pull/push commands
|
|
3
|
-
*
|
|
4
|
-
* Handles API URL, authentication tokens, and project resolution.
|
|
5
|
-
* @module cli/shared/config
|
|
6
|
-
*/
|
|
7
1
|
import type { InferSchema } from "../../src/extensions/schema/index.js";
|
|
8
2
|
import { type EnvironmentConfig } from "../../src/config/index.js";
|
|
9
3
|
export declare const getVeryfrontConfigSchema: () => import("../../src/internal-agents/schema.js").Schema<import("../../src/extensions/schema/schema-validator.js").InferShape<{
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"config.d.ts","sourceRoot":"","sources":["../../../src/cli/shared/config.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"config.d.ts","sourceRoot":"","sources":["../../../src/cli/shared/config.ts"],"names":[],"mappings":"AAUA,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,sCAAsC,CAAC;AAGxE,OAAO,EAAE,KAAK,iBAAiB,EAAwB,MAAM,2BAA2B,CAAC;AA6BzF,eAAO,MAAM,wBAAwB;;IAGjC,mDAAmD;;;;GAKtD,CAAC;AACF,eAAO,MAAM,qBAAqB;;IAN9B,mDAAmD;;;;GAMkB,CAAC;AAC1E,MAAM,MAAM,eAAe,GAAG,WAAW,CAAC,UAAU,CAAC,OAAO,wBAAwB,CAAC,CAAC,CAAC;AAEvF,eAAO,MAAM,uBAAuB;;;;GAMnC,CAAC;AACF,eAAO,MAAM,oBAAoB;;;;GAAsC,CAAC;AACxE,MAAM,MAAM,cAAc,GAAG,WAAW,CAAC,UAAU,CAAC,OAAO,uBAAuB,CAAC,CAAC,CAAC;AAErF,wBAAsB,cAAc,CAAC,UAAU,EAAE,MAAM,GAAG,OAAO,CAAC,eAAe,GAAG,IAAI,CAAC,CA6BxF;AAED,wBAAsB,gBAAgB,CAAC,UAAU,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAatF;AA4ED,eAAO,MAAM,aAAa,gBAJH,MAAM,QAAQ,iBAAiB,KAAG,OAAO,CAAC,cAAc,CAIvB,CAAC;AAEzD;;;;;GAKG;AACH,eAAO,MAAM,qBAAqB,gBAZX,MAAM,QAAQ,iBAAiB,KAAG,OAAO,CAAC,cAAc,CAYhB,CAAC;AAUhE,MAAM,WAAW,SAAS;IACxB,GAAG,CAAC,CAAC,EAAE,IAAI,EAAE,MAAM,EAAE,MAAM,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,GAAG,OAAO,CAAC,CAAC,CAAC,CAAC;IAClE,IAAI,CAAC,CAAC,EAAE,IAAI,EAAE,MAAM,EAAE,IAAI,CAAC,EAAE,OAAO,GAAG,OAAO,CAAC,CAAC,CAAC,CAAC;IAClD,GAAG,CAAC,CAAC,EAAE,IAAI,EAAE,MAAM,EAAE,IAAI,CAAC,EAAE,OAAO,GAAG,OAAO,CAAC,CAAC,CAAC,CAAC;IACjD,KAAK,CAAC,CAAC,EAAE,IAAI,EAAE,MAAM,EAAE,IAAI,CAAC,EAAE,OAAO,GAAG,OAAO,CAAC,CAAC,CAAC,CAAC;IACnD,MAAM,CAAC,CAAC,EAAE,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC,CAAC,CAAC,CAAC;CACrC;AAED,eAAO,MAAM,iBAAiB;;;;GAM7B,CAAC;AACF,eAAO,MAAM,cAAc;;;;GAAgC,CAAC;AAC5D,MAAM,MAAM,QAAQ,GAAG,WAAW,CAAC,UAAU,CAAC,OAAO,iBAAiB,CAAC,CAAC,CAAC;AAEzE,wBAAgB,eAAe,CAAC,MAAM,EAAE,cAAc,GAAG,SAAS,CA+GjE"}
|
package/esm/cli/shared/config.js
CHANGED
|
@@ -4,14 +4,35 @@
|
|
|
4
4
|
* Handles API URL, authentication tokens, and project resolution.
|
|
5
5
|
* @module cli/shared/config
|
|
6
6
|
*/
|
|
7
|
+
import * as dntShim from "../../_dnt.shims.js";
|
|
7
8
|
import { defineSchema, lazySchema } from "../../src/schemas/index.js";
|
|
8
9
|
import { join } from "../../src/platform/compat/path/index.js";
|
|
9
10
|
import { createFileSystem, cwd, getEnv } from "../../src/platform/index.js";
|
|
10
11
|
import { getEnvironmentConfig } from "../../src/config/index.js";
|
|
11
|
-
import { cliLogger } from "../utils/index.js";
|
|
12
|
+
import { cliLogger, VERSION } from "../utils/index.js";
|
|
12
13
|
import { readToken } from "../auth/token-store.js";
|
|
13
14
|
import { ensureAuthenticated } from "../auth/login.js";
|
|
14
15
|
import { DEFAULT_API_URL } from "./constants.js";
|
|
16
|
+
import { isRetryableConnectionError } from "../../src/proxy/retry.js";
|
|
17
|
+
// Delays for exponential backoff with jitter: attempt 1 = ~300ms, 2 = ~1s, 3 = ~3s
|
|
18
|
+
const API_RETRY_DELAYS_MS = [300, 1000, 3000];
|
|
19
|
+
const API_MAX_RETRIES = 3;
|
|
20
|
+
/** Returns true for HTTP status codes that indicate a transient gateway failure. */
|
|
21
|
+
function isTransientStatus(status) {
|
|
22
|
+
return status === 502 || status === 503 || status === 504;
|
|
23
|
+
}
|
|
24
|
+
/** Returns true when the connection error is a refused connection (request never reached server). */
|
|
25
|
+
function isConnectionRefused(error) {
|
|
26
|
+
if (!(error instanceof Error))
|
|
27
|
+
return false;
|
|
28
|
+
const msg = error.message.toLowerCase();
|
|
29
|
+
return msg.includes("connection refused") || msg.includes("os error 111");
|
|
30
|
+
}
|
|
31
|
+
/** Sleep for `ms` milliseconds plus a random jitter up to 20% of `ms`. */
|
|
32
|
+
function sleepWithJitter(ms) {
|
|
33
|
+
const jitter = Math.floor(ms * 0.2 * Math.random());
|
|
34
|
+
return new Promise((resolve) => dntShim.setTimeout(resolve, ms + jitter));
|
|
35
|
+
}
|
|
15
36
|
export const getVeryfrontConfigSchema = defineSchema((v) => v.object({
|
|
16
37
|
projectSlug: v.string().optional(),
|
|
17
38
|
/** List of project slugs for multi-project pull */
|
|
@@ -144,17 +165,14 @@ export const getApiErrorSchema = defineSchema((v) => v.object({
|
|
|
144
165
|
export const ApiErrorSchema = lazySchema(getApiErrorSchema);
|
|
145
166
|
export function createApiClient(config) {
|
|
146
167
|
const { apiUrl, apiToken } = config;
|
|
147
|
-
async function
|
|
148
|
-
const
|
|
149
|
-
for (const [key, value] of Object.entries(params ?? {})) {
|
|
150
|
-
url.searchParams.set(key, value);
|
|
151
|
-
}
|
|
152
|
-
const response = await fetch(url.toString(), {
|
|
168
|
+
async function requestOnce(method, url, body) {
|
|
169
|
+
const response = await fetch(url, {
|
|
153
170
|
method,
|
|
154
171
|
headers: {
|
|
155
172
|
Authorization: `Bearer ${apiToken}`,
|
|
156
173
|
"Content-Type": "application/json",
|
|
157
174
|
Accept: "application/json",
|
|
175
|
+
"x-veryfront-client-version": VERSION,
|
|
158
176
|
},
|
|
159
177
|
body: body ? JSON.stringify(body) : undefined,
|
|
160
178
|
});
|
|
@@ -169,12 +187,50 @@ export function createApiClient(config) {
|
|
|
169
187
|
catch {
|
|
170
188
|
// Keep default error message if JSON parsing fails
|
|
171
189
|
}
|
|
172
|
-
|
|
190
|
+
const err = new Error(errorMessage);
|
|
191
|
+
err.status = response.status;
|
|
192
|
+
throw err;
|
|
173
193
|
}
|
|
174
194
|
if (response.status === 204)
|
|
175
195
|
return undefined;
|
|
176
196
|
return response.json();
|
|
177
197
|
}
|
|
198
|
+
/** Returns true for request methods that are safe to retry on any transient failure. */
|
|
199
|
+
function isIdempotent(method) {
|
|
200
|
+
return method === "GET" || method === "HEAD";
|
|
201
|
+
}
|
|
202
|
+
async function request(method, path, body, params) {
|
|
203
|
+
const url = new URL(`${apiUrl}${path}`);
|
|
204
|
+
for (const [key, value] of Object.entries(params ?? {})) {
|
|
205
|
+
url.searchParams.set(key, value);
|
|
206
|
+
}
|
|
207
|
+
const urlStr = url.toString();
|
|
208
|
+
let lastError;
|
|
209
|
+
for (let attempt = 0; attempt < API_MAX_RETRIES; attempt++) {
|
|
210
|
+
try {
|
|
211
|
+
return await requestOnce(method, urlStr, body);
|
|
212
|
+
}
|
|
213
|
+
catch (error) {
|
|
214
|
+
lastError = error;
|
|
215
|
+
const status = error.status;
|
|
216
|
+
const isTransient = status !== undefined
|
|
217
|
+
? isTransientStatus(status)
|
|
218
|
+
: isRetryableConnectionError(error);
|
|
219
|
+
const isRefused = isConnectionRefused(error);
|
|
220
|
+
// Idempotent: retry on transient HTTP status or any retryable connection error.
|
|
221
|
+
// Non-idempotent: retry only on connection-refused (request never reached server).
|
|
222
|
+
const shouldRetry = isIdempotent(method)
|
|
223
|
+
? (isTransient || isRetryableConnectionError(error))
|
|
224
|
+
: isRefused;
|
|
225
|
+
if (!shouldRetry || attempt >= API_MAX_RETRIES - 1) {
|
|
226
|
+
throw error;
|
|
227
|
+
}
|
|
228
|
+
await sleepWithJitter(API_RETRY_DELAYS_MS[attempt]);
|
|
229
|
+
cliLogger.debug(`API request ${method} ${path} failed (attempt ${attempt + 1}), retrying...`, error);
|
|
230
|
+
}
|
|
231
|
+
}
|
|
232
|
+
throw lastError;
|
|
233
|
+
}
|
|
178
234
|
return {
|
|
179
235
|
get(path, params) {
|
|
180
236
|
return request("GET", path, undefined, params);
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"integration-loader.d.ts","sourceRoot":"","sources":["../../../src/cli/templates/integration-loader.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;
|
|
1
|
+
{"version":3,"file":"integration-loader.d.ts","sourceRoot":"","sources":["../../../src/cli/templates/integration-loader.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AAYH,OAAO,KAAK,EACV,iBAAiB,EACjB,eAAe,EACf,mBAAmB,EACnB,YAAY,EACZ,aAAa,EACb,WAAW,EACZ,MAAM,YAAY,CAAC;AAEpB;;;;GAIG;AACH,eAAO,MAAM,0BAA0B,EAAE,eAAe,EAEvD,CAAC;AAEF;;;GAGG;AACH,eAAO,MAAM,sBAAsB,EAAE,eAAe,EAEK,CAAC;AAE1D,wBAAgB,wBAAwB,IAAI,eAAe,EAAE,CAI5D;AAED;;GAEG;AACH,eAAO,MAAM,kBAAkB,EAAE,WAAW,EAM3C,CAAC;AAEF;;GAEG;AACH,eAAO,MAAM,gBAAgB,EAAE,MAAM,CAAC,WAAW,EAAE,aAAa,CA8C/D,CAAC;AAMF;;GAEG;AACH,wBAAgB,uBAAuB,CAAC,eAAe,EAAE,MAAM,GAAG,MAAM,CAEvE;AAED;;GAEG;AACH,wBAAsB,qBAAqB,CACzC,eAAe,EAAE,eAAe,GAC/B,OAAO,CAAC,iBAAiB,GAAG,IAAI,CAAC,CAanC;AAED;;GAEG;AACH,wBAAsB,eAAe,CACnC,eAAe,EAAE,eAAe,GAC/B,OAAO,CAAC,mBAAmB,GAAG,IAAI,CAAC,CAOrC;AAED;;GAEG;AACH,wBAAgB,oBAAoB,CAAC,YAAY,EAAE,eAAe,EAAE,GAAG;IACrE,KAAK,EAAE,OAAO,CAAC;IACf,MAAM,EAAE,MAAM,EAAE,CAAC;CAClB,CAIA;AAED;;GAEG;AACH,wBAAsB,gBAAgB,CACpC,gBAAgB,EAAE,eAAe,EAAE,GAClC,OAAO,CAAC;IACT,YAAY,EAAE,mBAAmB,EAAE,CAAC;IACpC,KAAK,EAAE,YAAY,EAAE,CAAC;IACtB,MAAM,EAAE,MAAM,EAAE,CAAC;CAClB,CAAC,CAkBD;AAED;;GAEG;AACH,wBAAsB,iBAAiB,CAAC,eAAe,EAAE,MAAM,GAAG,OAAO,CAAC,OAAO,CAAC,CAUjF;AAED;;GAEG;AACH,wBAAgB,gBAAgB,CAAC,WAAW,EAAE,WAAW,GAAG,aAAa,CAExE;AAED;;GAEG;AACH,wBAAsB,mBAAmB,CACvC,gBAAgB,EAAE,eAAe,EAAE,GAClC,OAAO,CACR,KAAK,CAAC;IACJ,WAAW,EAAE,eAAe,CAAC;IAC7B,OAAO,EAAE,iBAAiB,CAAC,SAAS,CAAC,CAAC;CACvC,CAAC,CACH,CAcA;AAED;;;GAGG;AACH,wBAAgB,qCAAqC,IAAI,OAAO,CAAC,YAAY,EAAE,CAAC,CAE/E;AAED;;GAEG;AACH,wBAAgB,yBAAyB,IAAI,OAAO,CAAC,iBAAiB,GAAG,IAAI,CAAC,CAE7E;AAED;;;GAGG;AACH,wBAAgB,uBAAuB,IAAI,YAAY,EAAE,CAwZxD"}
|
|
@@ -9,6 +9,7 @@
|
|
|
9
9
|
*/
|
|
10
10
|
import { createFileSystem, join } from "../../src/fs/index.js";
|
|
11
11
|
import { filterVisibleIntegrations } from "../../src/integrations/feature-flags.js";
|
|
12
|
+
import { ALL_INTEGRATION_NAMES } from "../../src/integrations/schema.js";
|
|
12
13
|
import { loadTemplateFromDirectory } from "./loader.js";
|
|
13
14
|
import { buildIntegrationDirectory, buildUnknownIntegrationErrors, mergeIntegrationFiles, resolveIntegrationModuleDir, } from "./integration-loader-helpers.js";
|
|
14
15
|
/**
|
|
@@ -17,58 +18,7 @@ import { buildIntegrationDirectory, buildUnknownIntegrationErrors, mergeIntegrat
|
|
|
17
18
|
* VERYFRONT_EXPERIMENTAL_INTEGRATIONS.
|
|
18
19
|
*/
|
|
19
20
|
export const ALL_AVAILABLE_INTEGRATIONS = [
|
|
20
|
-
|
|
21
|
-
"slack",
|
|
22
|
-
"github",
|
|
23
|
-
"calendar",
|
|
24
|
-
"jira",
|
|
25
|
-
"notion",
|
|
26
|
-
"servicenow",
|
|
27
|
-
"confluence",
|
|
28
|
-
"linear",
|
|
29
|
-
"gitlab",
|
|
30
|
-
"outlook",
|
|
31
|
-
"teams",
|
|
32
|
-
"figma",
|
|
33
|
-
"sap",
|
|
34
|
-
"sheets",
|
|
35
|
-
"airtable",
|
|
36
|
-
"supabase",
|
|
37
|
-
"neon",
|
|
38
|
-
"sharepoint",
|
|
39
|
-
"stripe",
|
|
40
|
-
"salesforce",
|
|
41
|
-
"twitter",
|
|
42
|
-
"onedrive",
|
|
43
|
-
"bitbucket",
|
|
44
|
-
"sentry",
|
|
45
|
-
"posthog",
|
|
46
|
-
"persona",
|
|
47
|
-
"zendesk",
|
|
48
|
-
// New integrations
|
|
49
|
-
"asana",
|
|
50
|
-
"monday",
|
|
51
|
-
"zoom",
|
|
52
|
-
"trello",
|
|
53
|
-
"box",
|
|
54
|
-
"shopify",
|
|
55
|
-
"clickup",
|
|
56
|
-
"intercom",
|
|
57
|
-
"pipedrive",
|
|
58
|
-
"mailchimp",
|
|
59
|
-
"webex",
|
|
60
|
-
"freshdesk",
|
|
61
|
-
"quickbooks",
|
|
62
|
-
"xero",
|
|
63
|
-
// 50+ integrations
|
|
64
|
-
"drive",
|
|
65
|
-
"docs-google",
|
|
66
|
-
"snowflake",
|
|
67
|
-
"mixpanel",
|
|
68
|
-
"twilio",
|
|
69
|
-
"anthropic",
|
|
70
|
-
"aws",
|
|
71
|
-
"hubspot",
|
|
21
|
+
...ALL_INTEGRATION_NAMES,
|
|
72
22
|
];
|
|
73
23
|
/**
|
|
74
24
|
* Default available integrations that can be added via --integrations flag.
|
|
@@ -216,7 +216,7 @@ export default {
|
|
|
216
216
|
".env.example": "# Google Docs Integration\n# Create OAuth credentials at https://console.cloud.google.com/apis/credentials\n# Make sure to enable:\n# - Google Docs API: https://console.cloud.google.com/apis/library/docs.googleapis.com\n# - Google Drive API: https://console.cloud.google.com/apis/library/drive.googleapis.com\n\nGOOGLE_CLIENT_ID=your_client_id_here\nGOOGLE_CLIENT_SECRET=your_client_secret_here\n",
|
|
217
217
|
"app/api/auth/docs-google/callback/route.ts": "import { createOAuthCallbackHandler, docsGoogleConfig } from \"veryfront/oauth\";\nimport { tokenStore } from \"../../../../../lib/token-store.ts\";\nimport { oauthMemoryTokenStore } from \"../../../../../lib/oauth-memory-store.ts\";\n\nconst hybridTokenStore = {\n getTokens(serviceId: string, userId: string) {\n return tokenStore.getToken(userId, serviceId);\n },\n async setTokens(\n serviceId: string,\n userId: string,\n tokens: { accessToken: string; refreshToken?: string; expiresAt?: number },\n ) {\n await tokenStore.setToken(userId, serviceId, tokens);\n },\n async clearTokens(serviceId: string, userId: string) {\n await tokenStore.revokeToken(userId, serviceId);\n },\n setState(\n state: string,\n meta: {\n userId: string;\n serviceId: string;\n codeVerifier?: string;\n redirectUri?: string;\n scopes?: string[];\n createdAt: number;\n },\n ) {\n return oauthMemoryTokenStore.setState(state, meta);\n },\n consumeState(state: string) {\n return oauthMemoryTokenStore.consumeState(state);\n },\n};\n\nexport const GET = createOAuthCallbackHandler(docsGoogleConfig, { tokenStore: hybridTokenStore });\n",
|
|
218
218
|
"app/api/auth/docs-google/route.ts": "import { createOAuthInitHandler, docsGoogleConfig } from \"veryfront/oauth\";\nimport { oauthMemoryTokenStore } from \"../../../../../lib/oauth-memory-store.ts\";\nimport { requireUserIdFromRequest } from \"../../../../../lib/user-id.ts\";\n\nfunction getUserId(request: Request): string {\n return requireUserIdFromRequest(request);\n}\n\nexport const GET = createOAuthInitHandler(docsGoogleConfig, {\n tokenStore: oauthMemoryTokenStore,\n getUserId,\n});",
|
|
219
|
-
"lib/docs-client.ts": "/**\n * Google Docs API Client\n *\n * Provides a type-safe interface to Google Docs API operations.\n */\n\nimport { getValidToken } from \"./oauth.ts\";\n\nfunction getEnv(key: string): string | undefined {\n // @ts-ignore - Deno global\n if (typeof Deno !== \"undefined\") return Deno.env.get(key);\n // @ts-ignore - process global\n if (typeof process !== \"undefined\" && process.env) return process.env[key];\n return undefined;\n}\n\nconst DOCS_API_BASE = \"https://docs.googleapis.com/v1\";\nconst DRIVE_API_BASE = \"https://www.googleapis.com/drive/v3\";\n\nexport interface Document {\n documentId: string;\n title: string;\n body: {\n content: StructuralElement[];\n };\n revisionId: string;\n suggestionsViewMode: string;\n documentStyle: DocumentStyle;\n}\n\nexport interface StructuralElement {\n startIndex: number;\n endIndex: number;\n paragraph?: Paragraph;\n table?: Table;\n sectionBreak?: SectionBreak;\n}\n\nexport interface Paragraph {\n elements: ParagraphElement[];\n paragraphStyle?: ParagraphStyle;\n bullet?: Bullet;\n}\n\nexport interface ParagraphElement {\n startIndex: number;\n endIndex: number;\n textRun?: TextRun;\n inlineObjectElement?: InlineObjectElement;\n}\n\nexport interface TextRun {\n content: string;\n textStyle?: TextStyle;\n}\n\nexport interface TextStyle {\n bold?: boolean;\n italic?: boolean;\n underline?: boolean;\n strikethrough?: boolean;\n fontSize?: Dimension;\n foregroundColor?: Color;\n backgroundColor?: Color;\n fontFamily?: string;\n link?: Link;\n}\n\nexport interface Link {\n url?: string;\n bookmarkId?: string;\n headingId?: string;\n}\n\nexport interface Dimension {\n magnitude: number;\n unit: string;\n}\n\nexport interface Color {\n rgbColor?: RgbColor;\n}\n\nexport interface RgbColor {\n red: number;\n green: number;\n blue: number;\n}\n\nexport interface ParagraphStyle {\n headingId?: string;\n namedStyleType?: string;\n alignment?: string;\n lineSpacing?: number;\n direction?: string;\n spacingMode?: string;\n spaceAbove?: Dimension;\n spaceBelow?: Dimension;\n indentFirstLine?: Dimension;\n indentStart?: Dimension;\n indentEnd?: Dimension;\n}\n\nexport interface Bullet {\n listId: string;\n nestingLevel?: number;\n textStyle?: TextStyle;\n}\n\nexport interface Table {\n rows: number;\n columns: number;\n tableRows: TableRow[];\n tableStyle?: TableStyle;\n}\n\nexport interface TableRow {\n startIndex: number;\n endIndex: number;\n tableCells: TableCell[];\n}\n\nexport interface TableCell {\n startIndex: number;\n endIndex: number;\n content: StructuralElement[];\n tableCellStyle?: TableCellStyle;\n}\n\nexport interface TableCellStyle {\n rowSpan?: number;\n columnSpan?: number;\n backgroundColor?: Color;\n borderLeft?: TableCellBorder;\n borderRight?: TableCellBorder;\n borderTop?: TableCellBorder;\n borderBottom?: TableCellBorder;\n paddingLeft?: Dimension;\n paddingRight?: Dimension;\n paddingTop?: Dimension;\n paddingBottom?: Dimension;\n}\n\nexport interface TableCellBorder {\n color?: Color;\n width?: Dimension;\n dashStyle?: string;\n}\n\nexport interface TableStyle {\n tableColumnProperties?: TableColumnProperties[];\n}\n\nexport interface TableColumnProperties {\n width?: Dimension;\n widthType?: string;\n}\n\nexport interface SectionBreak {\n sectionStyle?: SectionStyle;\n}\n\nexport interface SectionStyle {\n columnSeparatorStyle?: string;\n contentDirection?: string;\n marginTop?: Dimension;\n marginBottom?: Dimension;\n marginRight?: Dimension;\n marginLeft?: Dimension;\n pageNumberStart?: number;\n}\n\nexport interface DocumentStyle {\n background?: Background;\n pageNumberStart?: number;\n marginTop?: Dimension;\n marginBottom?: Dimension;\n marginRight?: Dimension;\n marginLeft?: Dimension;\n pageSize?: Size;\n marginHeader?: Dimension;\n marginFooter?: Dimension;\n useFirstPageHeaderFooter?: boolean;\n}\n\nexport interface Background {\n color?: Color;\n}\n\nexport interface Size {\n height?: Dimension;\n width?: Dimension;\n}\n\nexport interface InlineObjectElement {\n inlineObjectId: string;\n textStyle?: TextStyle;\n}\n\nexport interface DocumentFile {\n id: string;\n name: string;\n mimeType: string;\n createdTime: string;\n modifiedTime: string;\n webViewLink: string;\n iconLink?: string;\n thumbnailLink?: string;\n}\n\nexport interface CreateDocumentOptions {\n title: string;\n}\n\nexport interface BatchUpdateRequest {\n requests: Request[];\n}\n\nexport interface Request {\n insertText?: InsertTextRequest;\n deleteContentRange?: DeleteContentRangeRequest;\n replaceAllText?: ReplaceAllTextRequest;\n updateTextStyle?: UpdateTextStyleRequest;\n updateParagraphStyle?: UpdateParagraphStyleRequest;\n insertPageBreak?: InsertPageBreakRequest;\n insertTable?: InsertTableRequest;\n deleteTableRow?: DeleteTableRowRequest;\n deleteTableColumn?: DeleteTableColumnRequest;\n createParagraphBullets?: CreateParagraphBulletsRequest;\n deleteParagraphBullets?: DeleteParagraphBulletsRequest;\n}\n\nexport interface InsertTextRequest {\n text: string;\n location: Location;\n}\n\nexport interface DeleteContentRangeRequest {\n range: Range;\n}\n\nexport interface ReplaceAllTextRequest {\n containsText: ContainsText;\n replaceText: string;\n}\n\nexport interface UpdateTextStyleRequest {\n range: Range;\n textStyle: TextStyle;\n fields: string;\n}\n\nexport interface UpdateParagraphStyleRequest {\n range: Range;\n paragraphStyle: ParagraphStyle;\n fields: string;\n}\n\nexport interface InsertPageBreakRequest {\n location: Location;\n}\n\nexport interface InsertTableRequest {\n rows: number;\n columns: number;\n location: Location;\n}\n\nexport interface DeleteTableRowRequest {\n tableCellLocation: TableCellLocation;\n}\n\nexport interface DeleteTableColumnRequest {\n tableCellLocation: TableCellLocation;\n}\n\nexport interface CreateParagraphBulletsRequest {\n range: Range;\n bulletPreset: string;\n}\n\nexport interface DeleteParagraphBulletsRequest {\n range: Range;\n}\n\nexport interface Location {\n index: number;\n segmentId?: string;\n}\n\nexport interface Range {\n startIndex: number;\n endIndex: number;\n segmentId?: string;\n}\n\nexport interface ContainsText {\n text: string;\n matchCase: boolean;\n}\n\nexport interface TableCellLocation {\n tableStartLocation: Location;\n rowIndex: number;\n columnIndex: number;\n}\n\nexport interface BatchUpdateResponse {\n documentId: string;\n replies: Reply[];\n writeControl?: WriteControl;\n}\n\nexport interface Reply {\n [key: string]: unknown;\n}\n\nexport interface WriteControl {\n requiredRevisionId: string;\n targetRevisionId: string;\n}\n\n/**\n * Google Docs OAuth provider configuration\n */\nexport const docsOAuthProvider = {\n name: \"docs-google\",\n authorizationUrl: \"https://accounts.google.com/o/oauth2/v2/auth\",\n tokenUrl: \"https://oauth2.googleapis.com/token\",\n clientId: getEnv(\"GOOGLE_CLIENT_ID\") ?? \"\",\n clientSecret: getEnv(\"GOOGLE_CLIENT_SECRET\") ?? \"\",\n scopes: [\n \"https://www.googleapis.com/auth/documents.readonly\",\n \"https://www.googleapis.com/auth/documents\",\n \"https://www.googleapis.com/auth/drive.readonly\",\n ],\n callbackPath: \"/api/auth/docs-google/callback\",\n};\n\nexport function createDocsClient(userId: string): {\n listDocuments(options?: {\n maxResults?: number;\n orderBy?: \"createdTime\" | \"modifiedTime\" | \"name\";\n }): Promise<DocumentFile[]>;\n getDocument(documentId: string): Promise<Document>;\n createDocument(options: CreateDocumentOptions): Promise<Document>;\n updateDocument(documentId: string, requests: Request[]): Promise<BatchUpdateResponse>;\n insertText(documentId: string, text: string, index: number): Promise<BatchUpdateResponse>;\n deleteContent(documentId: string, startIndex: number, endIndex: number): Promise<BatchUpdateResponse>;\n replaceAllText(\n documentId: string,\n searchText: string,\n replaceText: string,\n matchCase?: boolean,\n ): Promise<BatchUpdateResponse>;\n searchDocuments(query: string, maxResults?: number): Promise<DocumentFile[]>;\n extractText(document: Document): string;\n createDocumentWithContent(title: string, content: string): Promise<Document>;\n} {\n async function getAccessToken(): Promise<string> {\n const token = await getValidToken(docsOAuthProvider, userId, \"docs-google\");\n if (!token) throw new Error(\"Google Docs not connected. Please connect your Google account first.\");\n return token;\n }\n\n async function apiRequest<T>(\n baseUrl: string,\n label: string,\n endpoint: string,\n options: RequestInit = {},\n ): Promise<T> {\n const accessToken = await getAccessToken();\n\n const response = await fetch(`${baseUrl}${endpoint}`, {\n ...options,\n headers: {\n Authorization: `Bearer ${accessToken}`,\n \"Content-Type\": \"application/json\",\n ...options.headers,\n },\n });\n\n if (!response.ok) {\n const error = await response.text();\n throw new Error(`${label} API error: ${response.status} - ${error}`);\n }\n\n return response.json();\n }\n\n function docsApiRequest<T>(endpoint: string, options: RequestInit = {}): Promise<T> {\n return apiRequest<T>(DOCS_API_BASE, \"Docs\", endpoint, options);\n }\n\n function driveApiRequest<T>(endpoint: string, options: RequestInit = {}): Promise<T> {\n return apiRequest<T>(DRIVE_API_BASE, \"Drive\", endpoint, options);\n }\n\n function extractText(document: Document): string {\n const textParts: string[] = [];\n\n function processElement(element: StructuralElement): void {\n if (element.paragraph) {\n for (const el of element.paragraph.elements) {\n if (el.textRun) textParts.push(el.textRun.content);\n }\n return;\n }\n\n if (!element.table) return;\n\n for (const row of element.table.tableRows) {\n for (const cell of row.tableCells) {\n for (const child of cell.content) processElement(child);\n }\n }\n }\n\n for (const element of document.body.content) processElement(element);\n return textParts.join(\"\");\n }\n\n async function listDocuments(options: {\n maxResults?: number;\n orderBy?: \"createdTime\" | \"modifiedTime\" | \"name\";\n } = {}): Promise<DocumentFile[]> {\n const params = new URLSearchParams({\n q: \"mimeType='application/vnd.google-apps.document' and trashed=false\",\n fields: \"files(id,name,mimeType,createdTime,modifiedTime,webViewLink,iconLink,thumbnailLink)\",\n pageSize: String(options.maxResults ?? 20),\n orderBy: `${options.orderBy ?? \"modifiedTime\"} desc`,\n });\n\n const result = await driveApiRequest<{ files: DocumentFile[] }>(`/files?${params.toString()}`);\n return result.files ?? [];\n }\n\n async function searchDocuments(query: string, maxResults = 20): Promise<DocumentFile[]> {\n const params = new URLSearchParams({\n q: `mimeType='application/vnd.google-apps.document' and trashed=false and fullText contains '${query}'`,\n fields: \"files(id,name,mimeType,createdTime,modifiedTime,webViewLink,iconLink,thumbnailLink)\",\n pageSize: String(maxResults),\n orderBy: \"modifiedTime desc\",\n });\n\n const result = await driveApiRequest<{ files: DocumentFile[] }>(`/files?${params.toString()}`);\n return result.files ?? [];\n }\n\n function getDocument(documentId: string): Promise<Document> {\n return docsApiRequest<Document>(`/documents/${documentId}`);\n }\n\n function createDocument(options: CreateDocumentOptions): Promise<Document> {\n return docsApiRequest<Document>(\"/documents\", {\n method: \"POST\",\n body: JSON.stringify({ title: options.title }),\n });\n }\n\n function updateDocument(documentId: string, requests: Request[]): Promise<BatchUpdateResponse> {\n return docsApiRequest<BatchUpdateResponse>(`/documents/${documentId}:batchUpdate`, {\n method: \"POST\",\n body: JSON.stringify({ requests }),\n });\n }\n\n function insertText(documentId: string, text: string, index: number): Promise<BatchUpdateResponse> {\n return updateDocument(documentId, [\n {\n insertText: {\n text,\n location: { index },\n },\n },\n ]);\n }\n\n function deleteContent(documentId: string, startIndex: number, endIndex: number): Promise<BatchUpdateResponse> {\n return updateDocument(documentId, [\n {\n deleteContentRange: {\n range: { startIndex, endIndex },\n },\n },\n ]);\n }\n\n function replaceAllText(\n documentId: string,\n searchText: string,\n replaceText: string,\n matchCase = false,\n ): Promise<BatchUpdateResponse> {\n return updateDocument(documentId, [\n {\n replaceAllText: {\n containsText: {\n text: searchText,\n matchCase,\n },\n replaceText,\n },\n },\n ]);\n }\n\n async function createDocumentWithContent(title: string, content: string): Promise<Document> {\n const doc = await createDocument({ title });\n await insertText(doc.documentId, content, 1);\n return getDocument(doc.documentId);\n }\n\n return {\n listDocuments,\n getDocument,\n createDocument,\n updateDocument,\n insertText,\n deleteContent,\n replaceAllText,\n searchDocuments,\n extractText,\n createDocumentWithContent,\n };\n}\n\nexport type DocsClient = ReturnType<typeof createDocsClient>;\n",
|
|
219
|
+
"lib/docs-client.ts": "/**\n * Google Docs API Client\n *\n * Provides a type-safe interface to Google Docs API operations.\n */\n\nimport { getValidToken } from \"./oauth.ts\";\n\nfunction getEnv(key: string): string | undefined {\n // @ts-ignore - Deno global\n if (typeof Deno !== \"undefined\") return Deno.env.get(key);\n // @ts-ignore - process global\n if (typeof process !== \"undefined\" && process.env) return process.env[key];\n return undefined;\n}\n\nconst DOCS_API_BASE = \"https://docs.googleapis.com/v1\";\nconst DRIVE_API_BASE = \"https://www.googleapis.com/drive/v3\";\n\nexport interface Document {\n documentId: string;\n title: string;\n body: {\n content: StructuralElement[];\n };\n revisionId: string;\n suggestionsViewMode: string;\n documentStyle: DocumentStyle;\n}\n\nexport interface StructuralElement {\n startIndex: number;\n endIndex: number;\n paragraph?: Paragraph;\n table?: Table;\n sectionBreak?: SectionBreak;\n}\n\nexport interface Paragraph {\n elements: ParagraphElement[];\n paragraphStyle?: ParagraphStyle;\n bullet?: Bullet;\n}\n\nexport interface ParagraphElement {\n startIndex: number;\n endIndex: number;\n textRun?: TextRun;\n inlineObjectElement?: InlineObjectElement;\n}\n\nexport interface TextRun {\n content: string;\n textStyle?: TextStyle;\n}\n\nexport interface TextStyle {\n bold?: boolean;\n italic?: boolean;\n underline?: boolean;\n strikethrough?: boolean;\n fontSize?: Dimension;\n foregroundColor?: Color;\n backgroundColor?: Color;\n fontFamily?: string;\n link?: Link;\n}\n\nexport interface Link {\n url?: string;\n bookmarkId?: string;\n headingId?: string;\n}\n\nexport interface Dimension {\n magnitude: number;\n unit: string;\n}\n\nexport interface Color {\n rgbColor?: RgbColor;\n}\n\nexport interface RgbColor {\n red: number;\n green: number;\n blue: number;\n}\n\nexport interface ParagraphStyle {\n headingId?: string;\n namedStyleType?: string;\n alignment?: string;\n lineSpacing?: number;\n direction?: string;\n spacingMode?: string;\n spaceAbove?: Dimension;\n spaceBelow?: Dimension;\n indentFirstLine?: Dimension;\n indentStart?: Dimension;\n indentEnd?: Dimension;\n}\n\nexport interface Bullet {\n listId: string;\n nestingLevel?: number;\n textStyle?: TextStyle;\n}\n\nexport interface Table {\n rows: number;\n columns: number;\n tableRows: TableRow[];\n tableStyle?: TableStyle;\n}\n\nexport interface TableRow {\n startIndex: number;\n endIndex: number;\n tableCells: TableCell[];\n}\n\nexport interface TableCell {\n startIndex: number;\n endIndex: number;\n content: StructuralElement[];\n tableCellStyle?: TableCellStyle;\n}\n\nexport interface TableCellStyle {\n rowSpan?: number;\n columnSpan?: number;\n backgroundColor?: Color;\n borderLeft?: TableCellBorder;\n borderRight?: TableCellBorder;\n borderTop?: TableCellBorder;\n borderBottom?: TableCellBorder;\n paddingLeft?: Dimension;\n paddingRight?: Dimension;\n paddingTop?: Dimension;\n paddingBottom?: Dimension;\n}\n\nexport interface TableCellBorder {\n color?: Color;\n width?: Dimension;\n dashStyle?: string;\n}\n\nexport interface TableStyle {\n tableColumnProperties?: TableColumnProperties[];\n}\n\nexport interface TableColumnProperties {\n width?: Dimension;\n widthType?: string;\n}\n\nexport interface SectionBreak {\n sectionStyle?: SectionStyle;\n}\n\nexport interface SectionStyle {\n columnSeparatorStyle?: string;\n contentDirection?: string;\n marginTop?: Dimension;\n marginBottom?: Dimension;\n marginRight?: Dimension;\n marginLeft?: Dimension;\n pageNumberStart?: number;\n}\n\nexport interface DocumentStyle {\n background?: Background;\n pageNumberStart?: number;\n marginTop?: Dimension;\n marginBottom?: Dimension;\n marginRight?: Dimension;\n marginLeft?: Dimension;\n pageSize?: Size;\n marginHeader?: Dimension;\n marginFooter?: Dimension;\n useFirstPageHeaderFooter?: boolean;\n}\n\nexport interface Background {\n color?: Color;\n}\n\nexport interface Size {\n height?: Dimension;\n width?: Dimension;\n}\n\nexport interface InlineObjectElement {\n inlineObjectId: string;\n textStyle?: TextStyle;\n}\n\nexport interface DocumentFile {\n id: string;\n name: string;\n mimeType: string;\n createdTime: string;\n modifiedTime: string;\n webViewLink: string;\n iconLink?: string;\n thumbnailLink?: string;\n}\n\nexport interface CreateDocumentOptions {\n title: string;\n}\n\nexport interface BatchUpdateRequest {\n requests: Request[];\n}\n\nexport interface Request {\n insertText?: InsertTextRequest;\n deleteContentRange?: DeleteContentRangeRequest;\n replaceAllText?: ReplaceAllTextRequest;\n updateTextStyle?: UpdateTextStyleRequest;\n updateParagraphStyle?: UpdateParagraphStyleRequest;\n insertPageBreak?: InsertPageBreakRequest;\n insertTable?: InsertTableRequest;\n deleteTableRow?: DeleteTableRowRequest;\n deleteTableColumn?: DeleteTableColumnRequest;\n createParagraphBullets?: CreateParagraphBulletsRequest;\n deleteParagraphBullets?: DeleteParagraphBulletsRequest;\n}\n\nexport interface InsertTextRequest {\n text: string;\n location: Location;\n}\n\nexport interface DeleteContentRangeRequest {\n range: Range;\n}\n\nexport interface ReplaceAllTextRequest {\n containsText: ContainsText;\n replaceText: string;\n}\n\nexport interface UpdateTextStyleRequest {\n range: Range;\n textStyle: TextStyle;\n fields: string;\n}\n\nexport interface UpdateParagraphStyleRequest {\n range: Range;\n paragraphStyle: ParagraphStyle;\n fields: string;\n}\n\nexport interface InsertPageBreakRequest {\n location: Location;\n}\n\nexport interface InsertTableRequest {\n rows: number;\n columns: number;\n location: Location;\n}\n\nexport interface DeleteTableRowRequest {\n tableCellLocation: TableCellLocation;\n}\n\nexport interface DeleteTableColumnRequest {\n tableCellLocation: TableCellLocation;\n}\n\nexport interface CreateParagraphBulletsRequest {\n range: Range;\n bulletPreset: string;\n}\n\nexport interface DeleteParagraphBulletsRequest {\n range: Range;\n}\n\nexport interface Location {\n index: number;\n segmentId?: string;\n}\n\nexport interface Range {\n startIndex: number;\n endIndex: number;\n segmentId?: string;\n}\n\nexport interface ContainsText {\n text: string;\n matchCase: boolean;\n}\n\nexport interface TableCellLocation {\n tableStartLocation: Location;\n rowIndex: number;\n columnIndex: number;\n}\n\nexport interface BatchUpdateResponse {\n documentId: string;\n replies: Reply[];\n writeControl?: WriteControl;\n}\n\nexport interface Reply {\n [key: string]: unknown;\n}\n\nexport interface WriteControl {\n requiredRevisionId: string;\n targetRevisionId: string;\n}\n\n/**\n * Google Docs OAuth provider configuration\n */\nexport const docsOAuthProvider = {\n name: \"docs-google\",\n authorizationUrl: \"https://accounts.google.com/o/oauth2/v2/auth\",\n tokenUrl: \"https://oauth2.googleapis.com/token\",\n clientId: getEnv(\"GOOGLE_CLIENT_ID\") ?? \"\",\n clientSecret: getEnv(\"GOOGLE_CLIENT_SECRET\") ?? \"\",\n scopes: [\n \"https://www.googleapis.com/auth/documents.readonly\",\n \"https://www.googleapis.com/auth/documents\",\n \"https://www.googleapis.com/auth/docs\",\n \"https://www.googleapis.com/auth/drive.readonly\",\n ],\n callbackPath: \"/api/auth/docs-google/callback\",\n};\n\nexport function createDocsClient(userId: string): {\n listDocuments(options?: {\n maxResults?: number;\n orderBy?: \"createdTime\" | \"modifiedTime\" | \"name\";\n }): Promise<DocumentFile[]>;\n getDocument(documentId: string): Promise<Document>;\n createDocument(options: CreateDocumentOptions): Promise<Document>;\n updateDocument(documentId: string, requests: Request[]): Promise<BatchUpdateResponse>;\n insertText(documentId: string, text: string, index: number): Promise<BatchUpdateResponse>;\n deleteContent(documentId: string, startIndex: number, endIndex: number): Promise<BatchUpdateResponse>;\n replaceAllText(\n documentId: string,\n searchText: string,\n replaceText: string,\n matchCase?: boolean,\n ): Promise<BatchUpdateResponse>;\n searchDocuments(query: string, maxResults?: number): Promise<DocumentFile[]>;\n extractText(document: Document): string;\n createDocumentWithContent(title: string, content: string): Promise<Document>;\n} {\n async function getAccessToken(): Promise<string> {\n const token = await getValidToken(docsOAuthProvider, userId, \"docs-google\");\n if (!token) throw new Error(\"Google Docs not connected. Please connect your Google account first.\");\n return token;\n }\n\n async function apiRequest<T>(\n baseUrl: string,\n label: string,\n endpoint: string,\n options: RequestInit = {},\n ): Promise<T> {\n const accessToken = await getAccessToken();\n\n const response = await fetch(`${baseUrl}${endpoint}`, {\n ...options,\n headers: {\n Authorization: `Bearer ${accessToken}`,\n \"Content-Type\": \"application/json\",\n ...options.headers,\n },\n });\n\n if (!response.ok) {\n const error = await response.text();\n throw new Error(`${label} API error: ${response.status} - ${error}`);\n }\n\n return response.json();\n }\n\n function docsApiRequest<T>(endpoint: string, options: RequestInit = {}): Promise<T> {\n return apiRequest<T>(DOCS_API_BASE, \"Docs\", endpoint, options);\n }\n\n function driveApiRequest<T>(endpoint: string, options: RequestInit = {}): Promise<T> {\n return apiRequest<T>(DRIVE_API_BASE, \"Drive\", endpoint, options);\n }\n\n function extractText(document: Document): string {\n const textParts: string[] = [];\n\n function processElement(element: StructuralElement): void {\n if (element.paragraph) {\n for (const el of element.paragraph.elements) {\n if (el.textRun) textParts.push(el.textRun.content);\n }\n return;\n }\n\n if (!element.table) return;\n\n for (const row of element.table.tableRows) {\n for (const cell of row.tableCells) {\n for (const child of cell.content) processElement(child);\n }\n }\n }\n\n for (const element of document.body.content) processElement(element);\n return textParts.join(\"\");\n }\n\n async function listDocuments(options: {\n maxResults?: number;\n orderBy?: \"createdTime\" | \"modifiedTime\" | \"name\";\n } = {}): Promise<DocumentFile[]> {\n const params = new URLSearchParams({\n q: \"mimeType='application/vnd.google-apps.document' and trashed=false\",\n fields: \"files(id,name,mimeType,createdTime,modifiedTime,webViewLink,iconLink,thumbnailLink)\",\n pageSize: String(options.maxResults ?? 20),\n orderBy: `${options.orderBy ?? \"modifiedTime\"} desc`,\n });\n\n const result = await driveApiRequest<{ files: DocumentFile[] }>(`/files?${params.toString()}`);\n return result.files ?? [];\n }\n\n async function searchDocuments(query: string, maxResults = 20): Promise<DocumentFile[]> {\n const params = new URLSearchParams({\n q: `mimeType='application/vnd.google-apps.document' and trashed=false and fullText contains '${query}'`,\n fields: \"files(id,name,mimeType,createdTime,modifiedTime,webViewLink,iconLink,thumbnailLink)\",\n pageSize: String(maxResults),\n orderBy: \"modifiedTime desc\",\n });\n\n const result = await driveApiRequest<{ files: DocumentFile[] }>(`/files?${params.toString()}`);\n return result.files ?? [];\n }\n\n function getDocument(documentId: string): Promise<Document> {\n return docsApiRequest<Document>(`/documents/${documentId}`);\n }\n\n function createDocument(options: CreateDocumentOptions): Promise<Document> {\n return docsApiRequest<Document>(\"/documents\", {\n method: \"POST\",\n body: JSON.stringify({ title: options.title }),\n });\n }\n\n function updateDocument(documentId: string, requests: Request[]): Promise<BatchUpdateResponse> {\n return docsApiRequest<BatchUpdateResponse>(`/documents/${documentId}:batchUpdate`, {\n method: \"POST\",\n body: JSON.stringify({ requests }),\n });\n }\n\n function insertText(documentId: string, text: string, index: number): Promise<BatchUpdateResponse> {\n return updateDocument(documentId, [\n {\n insertText: {\n text,\n location: { index },\n },\n },\n ]);\n }\n\n function deleteContent(documentId: string, startIndex: number, endIndex: number): Promise<BatchUpdateResponse> {\n return updateDocument(documentId, [\n {\n deleteContentRange: {\n range: { startIndex, endIndex },\n },\n },\n ]);\n }\n\n function replaceAllText(\n documentId: string,\n searchText: string,\n replaceText: string,\n matchCase = false,\n ): Promise<BatchUpdateResponse> {\n return updateDocument(documentId, [\n {\n replaceAllText: {\n containsText: {\n text: searchText,\n matchCase,\n },\n replaceText,\n },\n },\n ]);\n }\n\n async function createDocumentWithContent(title: string, content: string): Promise<Document> {\n const doc = await createDocument({ title });\n await insertText(doc.documentId, content, 1);\n return getDocument(doc.documentId);\n }\n\n return {\n listDocuments,\n getDocument,\n createDocument,\n updateDocument,\n insertText,\n deleteContent,\n replaceAllText,\n searchDocuments,\n extractText,\n createDocumentWithContent,\n };\n}\n\nexport type DocsClient = ReturnType<typeof createDocsClient>;\n",
|
|
220
220
|
"lib/oauth.ts": "import { type OAuthToken, tokenStore } from \"./token-store.ts\";\n\nexport interface OAuthProvider {\n name: string;\n authorizationUrl: string;\n tokenUrl: string;\n clientId: string;\n clientSecret: string;\n scopes: string[];\n callbackPath: string;\n}\n\nfunction getExpiresAt(expiresIn: unknown): number | undefined {\n if (typeof expiresIn !== \"number\") return undefined;\n return Date.now() + expiresIn * 1000;\n}\n\nasync function postForm(url: string, body: Record<string, string>): Promise<any> {\n const response = await fetch(url, {\n method: \"POST\",\n headers: { \"Content-Type\": \"application/x-www-form-urlencoded\" },\n body: new URLSearchParams(body),\n });\n\n if (response.ok) return response.json();\n\n throw new Error(\n `Token request failed: ${response.status} - ${await response.text()}`,\n );\n}\n\nexport function getAuthorizationUrl(\n provider: OAuthProvider,\n state: string,\n redirectUri: string,\n): string {\n const params = new URLSearchParams({\n client_id: provider.clientId,\n redirect_uri: redirectUri,\n response_type: \"code\",\n scope: provider.scopes.join(\" \"),\n state,\n access_type: \"offline\",\n prompt: \"consent\",\n });\n\n return `${provider.authorizationUrl}?${params.toString()}`;\n}\n\nexport async function exchangeCodeForTokens(\n provider: OAuthProvider,\n code: string,\n redirectUri: string,\n): Promise<OAuthToken> {\n const data = await postForm(provider.tokenUrl, {\n client_id: provider.clientId,\n client_secret: provider.clientSecret,\n code,\n grant_type: \"authorization_code\",\n redirect_uri: redirectUri,\n });\n\n return {\n accessToken: data.access_token,\n refreshToken: data.refresh_token,\n expiresAt: getExpiresAt(data.expires_in),\n tokenType: data.token_type ?? \"Bearer\",\n scope: data.scope,\n };\n}\n\nexport async function refreshAccessToken(\n provider: OAuthProvider,\n refreshToken: string,\n): Promise<OAuthToken> {\n const data = await postForm(provider.tokenUrl, {\n client_id: provider.clientId,\n client_secret: provider.clientSecret,\n refresh_token: refreshToken,\n grant_type: \"refresh_token\",\n });\n\n return {\n accessToken: data.access_token,\n refreshToken: data.refresh_token ?? refreshToken,\n expiresAt: getExpiresAt(data.expires_in),\n tokenType: data.token_type ?? \"Bearer\",\n scope: data.scope,\n };\n}\n\nexport async function getValidToken(\n provider: OAuthProvider,\n userId: string,\n service: string,\n): Promise<string | null> {\n const token = await tokenStore.getToken(userId, service);\n if (!token) return null;\n\n const isExpired = token.expiresAt\n ? token.expiresAt < Date.now() + 5 * 60 * 1000\n : false;\n\n if (!isExpired || !token.refreshToken) return token.accessToken;\n\n try {\n const newToken = await refreshAccessToken(provider, token.refreshToken);\n await tokenStore.setToken(userId, service, newToken);\n return newToken.accessToken;\n } catch {\n await tokenStore.revokeToken(userId, service);\n return null;\n }\n}\n",
|
|
221
221
|
"tools/create-document.ts": "import { tool } from \"veryfront/tool\";\nimport { defineSchema } from \"veryfront/schemas\";\nimport { createDocsClient } from \"../../lib/docs-client.ts\";\n\nconst DEFAULT_USER_ID = \"demo-user\";\n\nexport default tool({\n id: \"create-document\",\n description:\n \"Create a new Google Docs document with optional initial content. Returns the new document ID and URL.\",\n inputSchema: defineSchema((v) => v.object({\n title: v.string().describe(\"Title of the new document\"),\n content: v\n .string()\n .optional()\n .describe(\"Optional initial text content to insert into the document\"),\n }))(),\n async execute({ title, content }) {\n const client = createDocsClient(DEFAULT_USER_ID);\n\n const document = content\n ? await client.createDocumentWithContent(title, content)\n : await client.createDocument({ title });\n\n const [docMeta] = await client.listDocuments({ maxResults: 1 });\n const webViewLink = docMeta?.id === document.documentId ? docMeta.webViewLink : undefined;\n\n return {\n documentId: document.documentId,\n title: document.title,\n url: webViewLink ?? `https://docs.google.com/document/d/${document.documentId}/edit`,\n revisionId: document.revisionId,\n };\n },\n});\n",
|
|
222
222
|
"tools/get-document.ts": "import { tool } from \"veryfront/tool\";\nimport { defineSchema } from \"veryfront/schemas\";\nimport { createDocsClient } from \"../../lib/docs-client.ts\";\n\nconst DEFAULT_USER_ID = \"demo-user\";\n\nexport default tool({\n id: \"get-document\",\n description:\n \"Get a Google Docs document's content and metadata. Returns the full document structure including text, formatting, and styles.\",\n inputSchema: defineSchema((v) => v.object({\n documentId: v.string().describe(\"The ID of the document to retrieve\"),\n extractTextOnly: v\n .boolean()\n .default(false)\n .describe(\"If true, only return plain text content without formatting\"),\n }))(),\n async execute({ documentId, extractTextOnly }) {\n const client = createDocsClient(DEFAULT_USER_ID);\n const document = await client.getDocument(documentId);\n\n const { documentId: id, title, revisionId } = document;\n\n if (extractTextOnly) {\n return {\n documentId: id,\n title,\n revisionId,\n text: client.extractText(document),\n };\n }\n\n return {\n documentId: id,\n title,\n revisionId,\n body: document.body,\n documentStyle: document.documentStyle,\n };\n },\n});\n",
|
|
@@ -230,7 +230,7 @@ export default {
|
|
|
230
230
|
".env.example": "# Google Drive Integration\n# Create OAuth credentials at https://console.cloud.google.com/apis/credentials\n# Make sure to enable:\n# - Google Drive API: https://console.cloud.google.com/apis/library/drive.googleapis.com\n#\n# Note: These credentials are shared across all Google integrations (Gmail, Calendar, Sheets, Drive)\n\nGOOGLE_CLIENT_ID=your_client_id_here\nGOOGLE_CLIENT_SECRET=your_client_secret_here\n",
|
|
231
231
|
"app/api/auth/drive/callback/route.ts": "/**\n * Google Drive OAuth Callback\n *\n * Handles the OAuth callback from Google and stores the tokens.\n */\n\nimport { createOAuthCallbackHandler, driveConfig } from \"veryfront/oauth\";\nimport { tokenStore } from \"../../../../../lib/token-store.ts\";\nimport { oauthMemoryTokenStore } from \"../../../../../lib/oauth-memory-store.ts\";\n\nconst hybridTokenStore = {\n getTokens(serviceId: string, userId: string) {\n return tokenStore.getToken(userId, serviceId);\n },\n async setTokens(\n serviceId: string,\n userId: string,\n tokens: { accessToken: string; refreshToken?: string; expiresAt?: number },\n ) {\n await tokenStore.setToken(userId, serviceId, tokens);\n },\n async clearTokens(serviceId: string, userId: string) {\n await tokenStore.revokeToken(userId, serviceId);\n },\n setState(\n state: string,\n meta: {\n userId: string;\n serviceId: string;\n codeVerifier?: string;\n redirectUri?: string;\n scopes?: string[];\n createdAt: number;\n },\n ) {\n return oauthMemoryTokenStore.setState(state, meta);\n },\n consumeState(state: string) {\n return oauthMemoryTokenStore.consumeState(state);\n },\n};\n\nexport const GET = createOAuthCallbackHandler(driveConfig, { tokenStore: hybridTokenStore });\n",
|
|
232
232
|
"app/api/auth/drive/route.ts": "import { createOAuthInitHandler, driveConfig } from \"veryfront/oauth\";\nimport { oauthMemoryTokenStore } from \"../../../../../lib/oauth-memory-store.ts\";\nimport { requireUserIdFromRequest } from \"../../../../../lib/user-id.ts\";\n\nfunction getUserId(request: Request): string {\n return requireUserIdFromRequest(request);\n}\n\nexport const GET = createOAuthInitHandler(driveConfig, {\n tokenStore: oauthMemoryTokenStore,\n getUserId,\n});",
|
|
233
|
-
"lib/drive-client.ts": "import { getValidToken } from \"./oauth.ts\";\n\nfunction getEnv(key: string): string | undefined {\n // @ts-ignore - Deno global\n if (typeof Deno !== \"undefined\") return Deno.env.get(key);\n // @ts-ignore - process global\n if (typeof process !== \"undefined\" && process.env) return process.env[key];\n return undefined;\n}\n\nconst DRIVE_API_BASE = \"https://www.googleapis.com/drive/v3\";\n\nexport interface DriveFile {\n id: string;\n name: string;\n mimeType: string;\n kind: string;\n createdTime: string;\n modifiedTime: string;\n size?: string;\n webViewLink?: string;\n webContentLink?: string;\n iconLink?: string;\n thumbnailLink?: string;\n parents?: string[];\n starred?: boolean;\n trashed?: boolean;\n shared?: boolean;\n owners?: Array<{\n displayName: string;\n emailAddress: string;\n photoLink?: string;\n }>;\n lastModifyingUser?: {\n displayName: string;\n emailAddress: string;\n photoLink?: string;\n };\n capabilities?: {\n canEdit?: boolean;\n canComment?: boolean;\n canShare?: boolean;\n canDelete?: boolean;\n canDownload?: boolean;\n };\n}\n\nexport interface DriveFileList {\n files: DriveFile[];\n nextPageToken?: string;\n incompleteSearch?: boolean;\n}\n\nexport interface CreateFolderOptions {\n name: string;\n parentId?: string;\n description?: string;\n}\n\nexport interface UploadFileOptions {\n name: string;\n content: string;\n mimeType: string;\n parentId?: string;\n description?: string;\n}\n\nexport interface ListFilesOptions {\n folderId?: string;\n pageSize?: number;\n pageToken?: string;\n orderBy?: string;\n query?: string;\n}\n\nexport interface SearchFilesOptions {\n query: string;\n pageSize?: number;\n pageToken?: string;\n orderBy?: string;\n}\n\nexport const driveOAuthProvider = {\n name: \"drive\",\n authorizationUrl: \"https://accounts.google.com/o/oauth2/v2/auth\",\n tokenUrl: \"https://oauth2.googleapis.com/token\",\n clientId: getEnv(\"GOOGLE_CLIENT_ID\") ?? \"\",\n clientSecret: getEnv(\"GOOGLE_CLIENT_SECRET\") ?? \"\",\n scopes: [\n \"https://www.googleapis.com/auth/drive
|
|
233
|
+
"lib/drive-client.ts": "import { getValidToken } from \"./oauth.ts\";\n\nfunction getEnv(key: string): string | undefined {\n // @ts-ignore - Deno global\n if (typeof Deno !== \"undefined\") return Deno.env.get(key);\n // @ts-ignore - process global\n if (typeof process !== \"undefined\" && process.env) return process.env[key];\n return undefined;\n}\n\nconst DRIVE_API_BASE = \"https://www.googleapis.com/drive/v3\";\n\nexport interface DriveFile {\n id: string;\n name: string;\n mimeType: string;\n kind: string;\n createdTime: string;\n modifiedTime: string;\n size?: string;\n webViewLink?: string;\n webContentLink?: string;\n iconLink?: string;\n thumbnailLink?: string;\n parents?: string[];\n starred?: boolean;\n trashed?: boolean;\n shared?: boolean;\n owners?: Array<{\n displayName: string;\n emailAddress: string;\n photoLink?: string;\n }>;\n lastModifyingUser?: {\n displayName: string;\n emailAddress: string;\n photoLink?: string;\n };\n capabilities?: {\n canEdit?: boolean;\n canComment?: boolean;\n canShare?: boolean;\n canDelete?: boolean;\n canDownload?: boolean;\n };\n}\n\nexport interface DriveFileList {\n files: DriveFile[];\n nextPageToken?: string;\n incompleteSearch?: boolean;\n}\n\nexport interface CreateFolderOptions {\n name: string;\n parentId?: string;\n description?: string;\n}\n\nexport interface UploadFileOptions {\n name: string;\n content: string;\n mimeType: string;\n parentId?: string;\n description?: string;\n}\n\nexport interface ListFilesOptions {\n folderId?: string;\n pageSize?: number;\n pageToken?: string;\n orderBy?: string;\n query?: string;\n}\n\nexport interface SearchFilesOptions {\n query: string;\n pageSize?: number;\n pageToken?: string;\n orderBy?: string;\n}\n\nexport const driveOAuthProvider = {\n name: \"drive\",\n authorizationUrl: \"https://accounts.google.com/o/oauth2/v2/auth\",\n tokenUrl: \"https://oauth2.googleapis.com/token\",\n clientId: getEnv(\"GOOGLE_CLIENT_ID\") ?? \"\",\n clientSecret: getEnv(\"GOOGLE_CLIENT_SECRET\") ?? \"\",\n scopes: [\n \"https://www.googleapis.com/auth/drive\",\n ],\n callbackPath: \"/api/auth/drive/callback\",\n};\n\nexport function createDriveClient(userId: string): {\n listFiles(options?: ListFilesOptions): Promise<DriveFileList>;\n getFile(fileId: string): Promise<DriveFile>;\n searchFiles(options: SearchFilesOptions): Promise<DriveFileList>;\n createFolder(options: CreateFolderOptions): Promise<DriveFile>;\n uploadFile(options: UploadFileOptions): Promise<DriveFile>;\n downloadFile(fileId: string): Promise<string>;\n deleteFile(fileId: string): Promise<void>;\n copyFile(fileId: string, name: string, parentId?: string): Promise<DriveFile>;\n updateFile(\n fileId: string,\n updates: { name?: string; description?: string; starred?: boolean },\n ): Promise<DriveFile>;\n} {\n async function getAccessToken(): Promise<string> {\n const token = await getValidToken(driveOAuthProvider, userId, \"drive\");\n if (!token) {\n throw new Error(\"Google Drive not connected. Please connect your Google account first.\");\n }\n return token;\n }\n\n async function driveApiRequest<T>(endpoint: string, options: RequestInit = {}): Promise<T> {\n const accessToken = await getAccessToken();\n\n const response = await fetch(`${DRIVE_API_BASE}${endpoint}`, {\n ...options,\n headers: {\n Authorization: `Bearer ${accessToken}`,\n \"Content-Type\": \"application/json\",\n ...options.headers,\n },\n });\n\n if (!response.ok) {\n const error = await response.text();\n throw new Error(`Drive API error: ${response.status} - ${error}`);\n }\n\n if (response.status === 204) return undefined as T;\n return response.json();\n }\n\n function buildMetadata(options: {\n name: string;\n mimeType: string;\n parentId?: string;\n description?: string;\n }): Record<string, unknown> {\n const metadata: Record<string, unknown> = {\n name: options.name,\n mimeType: options.mimeType,\n };\n\n if (options.parentId) metadata.parents = [options.parentId];\n if (options.description) metadata.description = options.description;\n\n return metadata;\n }\n\n const fileFields =\n \"id,name,mimeType,kind,createdTime,modifiedTime,size,webViewLink,webContentLink,iconLink,thumbnailLink,parents,starred,trashed,shared,owners,lastModifyingUser,capabilities\";\n\n return {\n async listFiles(options: ListFilesOptions = {}): Promise<DriveFileList> {\n const params = new URLSearchParams({\n fields: `nextPageToken,incompleteSearch,files(${fileFields})`,\n pageSize: String(options.pageSize ?? 100),\n orderBy: options.orderBy ?? \"modifiedTime desc\",\n });\n\n let query = \"trashed=false\";\n if (options.folderId) query += ` and '${options.folderId}' in parents`;\n if (options.query) query += ` and ${options.query}`;\n\n params.append(\"q\", query);\n if (options.pageToken) params.append(\"pageToken\", options.pageToken);\n\n return driveApiRequest<DriveFileList>(`/files?${params.toString()}`);\n },\n\n async getFile(fileId: string): Promise<DriveFile> {\n const params = new URLSearchParams({ fields: fileFields });\n return driveApiRequest<DriveFile>(`/files/${fileId}?${params.toString()}`);\n },\n\n async searchFiles(options: SearchFilesOptions): Promise<DriveFileList> {\n const params = new URLSearchParams({\n fields:\n \"nextPageToken,incompleteSearch,files(id,name,mimeType,kind,createdTime,modifiedTime,size,webViewLink,webContentLink,iconLink,thumbnailLink,parents,starred,trashed)\",\n pageSize: String(options.pageSize ?? 100),\n q: `${options.query} and trashed=false`,\n orderBy: options.orderBy ?? \"modifiedTime desc\",\n });\n\n if (options.pageToken) params.append(\"pageToken\", options.pageToken);\n\n return driveApiRequest<DriveFileList>(`/files?${params.toString()}`);\n },\n\n async createFolder(options: CreateFolderOptions): Promise<DriveFile> {\n const metadata = buildMetadata({\n name: options.name,\n mimeType: \"application/vnd.google-apps.folder\",\n parentId: options.parentId,\n description: options.description,\n });\n\n return driveApiRequest<DriveFile>(\"/files\", {\n method: \"POST\",\n body: JSON.stringify(metadata),\n });\n },\n\n async uploadFile(options: UploadFileOptions): Promise<DriveFile> {\n const accessToken = await getAccessToken();\n\n const boundary = \"-------314159265358979323846\";\n const delimiter = `\\r\\n--${boundary}\\r\\n`;\n const closeDelimiter = `\\r\\n--${boundary}--`;\n\n const metadata = buildMetadata({\n name: options.name,\n mimeType: options.mimeType,\n parentId: options.parentId,\n description: options.description,\n });\n\n const multipartRequestBody =\n delimiter +\n \"Content-Type: application/json\\r\\n\\r\\n\" +\n JSON.stringify(metadata) +\n delimiter +\n `Content-Type: ${options.mimeType}\\r\\n\\r\\n` +\n options.content +\n closeDelimiter;\n\n const response = await fetch(\n \"https://www.googleapis.com/upload/drive/v3/files?uploadType=multipart&fields=id,name,mimeType,kind,createdTime,modifiedTime,size,webViewLink,webContentLink\",\n {\n method: \"POST\",\n headers: {\n Authorization: `Bearer ${accessToken}`,\n \"Content-Type\": `multipart/related; boundary=${boundary}`,\n },\n body: multipartRequestBody,\n },\n );\n\n if (!response.ok) {\n const error = await response.text();\n throw new Error(`Drive upload error: ${response.status} - ${error}`);\n }\n\n return response.json();\n },\n\n async downloadFile(fileId: string): Promise<string> {\n const accessToken = await getAccessToken();\n\n const response = await fetch(`${DRIVE_API_BASE}/files/${fileId}?alt=media`, {\n headers: { Authorization: `Bearer ${accessToken}` },\n });\n\n if (!response.ok) {\n const error = await response.text();\n throw new Error(`Drive download error: ${response.status} - ${error}`);\n }\n\n return response.text();\n },\n\n async deleteFile(fileId: string): Promise<void> {\n await driveApiRequest(`/files/${fileId}`, { method: \"DELETE\" });\n },\n\n async copyFile(fileId: string, name: string, parentId?: string): Promise<DriveFile> {\n const metadata: Record<string, unknown> = { name };\n if (parentId) metadata.parents = [parentId];\n\n return driveApiRequest<DriveFile>(`/files/${fileId}/copy`, {\n method: \"POST\",\n body: JSON.stringify(metadata),\n });\n },\n\n async updateFile(\n fileId: string,\n updates: { name?: string; description?: string; starred?: boolean },\n ): Promise<DriveFile> {\n return driveApiRequest<DriveFile>(`/files/${fileId}`, {\n method: \"PATCH\",\n body: JSON.stringify(updates),\n });\n },\n };\n}\n\nexport type DriveClient = ReturnType<typeof createDriveClient>;\n",
|
|
234
234
|
"lib/oauth.ts": "import { type OAuthToken, tokenStore } from \"./token-store.ts\";\n\nexport interface OAuthProvider {\n name: string;\n authorizationUrl: string;\n tokenUrl: string;\n clientId: string;\n clientSecret: string;\n scopes: string[];\n callbackPath: string;\n}\n\nfunction buildTokenRequest(\n provider: OAuthProvider,\n body: Record<string, string>,\n): RequestInit {\n return {\n method: \"POST\",\n headers: { \"Content-Type\": \"application/x-www-form-urlencoded\" },\n body: new URLSearchParams({\n client_id: provider.clientId,\n client_secret: provider.clientSecret,\n ...body,\n }),\n };\n}\n\nasync function fetchToken(\n provider: OAuthProvider,\n body: Record<string, string>,\n errorPrefix: string,\n): Promise<any> {\n const response = await fetch(\n provider.tokenUrl,\n buildTokenRequest(provider, body),\n );\n\n if (response.ok) return response.json();\n\n const error = await response.text();\n throw new Error(`${errorPrefix}: ${response.status} - ${error}`);\n}\n\nfunction toOAuthToken(data: any, fallbackRefreshToken?: string): OAuthToken {\n const expiresIn = data.expires_in;\n\n return {\n accessToken: data.access_token,\n refreshToken: data.refresh_token ?? fallbackRefreshToken,\n expiresAt: expiresIn ? Date.now() + expiresIn * 1000 : undefined,\n tokenType: data.token_type ?? \"Bearer\",\n scope: data.scope,\n };\n}\n\nexport function getAuthorizationUrl(\n provider: OAuthProvider,\n state: string,\n redirectUri: string,\n): string {\n const params = new URLSearchParams({\n client_id: provider.clientId,\n redirect_uri: redirectUri,\n response_type: \"code\",\n scope: provider.scopes.join(\" \"),\n state,\n access_type: \"offline\",\n prompt: \"consent\",\n });\n\n return `${provider.authorizationUrl}?${params.toString()}`;\n}\n\nexport async function exchangeCodeForTokens(\n provider: OAuthProvider,\n code: string,\n redirectUri: string,\n): Promise<OAuthToken> {\n const data = await fetchToken(\n provider,\n {\n code,\n grant_type: \"authorization_code\",\n redirect_uri: redirectUri,\n },\n \"Token exchange failed\",\n );\n\n return toOAuthToken(data);\n}\n\nexport async function refreshAccessToken(\n provider: OAuthProvider,\n refreshToken: string,\n): Promise<OAuthToken> {\n const data = await fetchToken(\n provider,\n {\n refresh_token: refreshToken,\n grant_type: \"refresh_token\",\n },\n \"Token refresh failed\",\n );\n\n return toOAuthToken(data, refreshToken);\n}\n\nexport async function getValidToken(\n provider: OAuthProvider,\n userId: string,\n service: string,\n): Promise<string | null> {\n const token = await tokenStore.getToken(userId, service);\n if (!token) return null;\n\n const isExpired = token.expiresAt\n ? token.expiresAt < Date.now() + 5 * 60 * 1000\n : false;\n\n if (!isExpired) return token.accessToken;\n if (!token.refreshToken) return token.accessToken;\n\n try {\n const newToken = await refreshAccessToken(provider, token.refreshToken);\n await tokenStore.setToken(userId, service, newToken);\n return newToken.accessToken;\n } catch {\n await tokenStore.revokeToken(userId, service);\n return null;\n }\n}\n",
|
|
235
235
|
"tools/create-folder.ts": "import { tool } from \"veryfront/tool\";\nimport { defineSchema } from \"veryfront/schemas\";\nimport { createDriveClient } from \"../../lib/drive-client.ts\";\n\nconst DEFAULT_USER_ID = \"demo-user\";\n\nexport default tool({\n id: \"create-folder\",\n description:\n \"Create a new folder in Google Drive. Can optionally specify a parent folder. Returns the new folder ID and details.\",\n inputSchema: defineSchema((v) => v.object({\n name: v.string().describe(\"Name of the folder to create\"),\n parentId: v\n .string()\n .optional()\n .describe(\"ID of the parent folder. If not provided, creates in root.\"),\n description: v\n .string()\n .optional()\n .describe(\"Optional description for the folder\"),\n }))(),\n async execute({ name, parentId, description }) {\n const client = createDriveClient(DEFAULT_USER_ID);\n const folder = await client.createFolder({ name, parentId, description });\n\n return {\n id: folder.id,\n name: folder.name,\n mimeType: folder.mimeType,\n createdTime: folder.createdTime,\n modifiedTime: folder.modifiedTime,\n webViewLink: folder.webViewLink,\n parents: folder.parents,\n };\n },\n});\n",
|
|
236
236
|
"tools/get-file.ts": "import { tool } from \"veryfront/tool\";\nimport { defineSchema } from \"veryfront/schemas\";\nimport { createDriveClient } from \"../../lib/drive-client.ts\";\n\nconst DEFAULT_USER_ID = \"demo-user\";\nconst FOLDER_MIME_TYPE = \"application/vnd.google-apps.folder\";\n\nexport default tool({\n id: \"get-file\",\n description:\n \"Get detailed metadata about a specific file or folder in Google Drive. Returns detailed information including sharing settings, owners, and capabilities.\",\n inputSchema: defineSchema((v) => v.object({\n fileId: v.string().describe(\"The ID of the file or folder to retrieve\"),\n }))(),\n async execute({ fileId }) {\n const client = createDriveClient(DEFAULT_USER_ID);\n const file = await client.getFile(fileId);\n\n const lastModifyingUser = file.lastModifyingUser\n ? {\n name: file.lastModifyingUser.displayName,\n email: file.lastModifyingUser.emailAddress,\n photoLink: file.lastModifyingUser.photoLink,\n }\n : undefined;\n\n return {\n id: file.id,\n name: file.name,\n mimeType: file.mimeType,\n isFolder: file.mimeType === FOLDER_MIME_TYPE,\n size: file.size,\n createdTime: file.createdTime,\n modifiedTime: file.modifiedTime,\n webViewLink: file.webViewLink,\n webContentLink: file.webContentLink,\n iconLink: file.iconLink,\n thumbnailLink: file.thumbnailLink,\n parents: file.parents,\n starred: file.starred,\n trashed: file.trashed,\n shared: file.shared,\n owners: file.owners?.map((owner) => ({\n name: owner.displayName,\n email: owner.emailAddress,\n photoLink: owner.photoLink,\n })),\n lastModifyingUser,\n capabilities: file.capabilities,\n };\n },\n});\n",
|
package/esm/deno.d.ts
CHANGED
|
@@ -13,18 +13,12 @@ declare namespace _default {
|
|
|
13
13
|
"./context": string;
|
|
14
14
|
"./fonts": string;
|
|
15
15
|
"./chat": string;
|
|
16
|
-
"./chat/ag-ui": string;
|
|
17
|
-
"./chat/compat": string;
|
|
18
|
-
"./chat/protocol": string;
|
|
19
16
|
"./chat/types": string;
|
|
20
17
|
"./markdown": string;
|
|
21
18
|
"./mdx": string;
|
|
22
19
|
"./agent": string;
|
|
23
20
|
"./agent/identity": string;
|
|
24
21
|
"./agent/testing": string;
|
|
25
|
-
"./agent/conversation-bootstrap": string;
|
|
26
|
-
"./agent/durable": string;
|
|
27
|
-
"./agent/invoke-agent-child-runs": string;
|
|
28
22
|
"./channels/control-plane": string;
|
|
29
23
|
"./channels/invoke": string;
|
|
30
24
|
"./tool": string;
|
|
@@ -67,12 +61,7 @@ declare namespace _default {
|
|
|
67
61
|
"./testing/assert": string;
|
|
68
62
|
"./testing/bdd": string;
|
|
69
63
|
"./cli": string;
|
|
70
|
-
"./chat/conversation": string;
|
|
71
64
|
"./chat/message-prep": string;
|
|
72
|
-
"./chat/final-step-fallback": string;
|
|
73
|
-
"./chat/provider-errors": string;
|
|
74
|
-
"./chat/stream-watchdog": string;
|
|
75
|
-
"./agent/request-auth-cache": string;
|
|
76
65
|
"./server": string;
|
|
77
66
|
};
|
|
78
67
|
let imports: {
|
|
@@ -81,8 +70,6 @@ declare namespace _default {
|
|
|
81
70
|
"veryfront/context": string;
|
|
82
71
|
"veryfront/fonts": string;
|
|
83
72
|
"veryfront/chat": string;
|
|
84
|
-
"veryfront/chat/ag-ui": string;
|
|
85
|
-
"veryfront/chat/protocol": string;
|
|
86
73
|
"veryfront/chat/types": string;
|
|
87
74
|
"veryfront/markdown": string;
|
|
88
75
|
"veryfront/mdx": string;
|
|
@@ -163,7 +150,12 @@ declare namespace _default {
|
|
|
163
150
|
"#veryfront/agent": string;
|
|
164
151
|
"#veryfront/agent/identity": string;
|
|
165
152
|
"#veryfront/agent/react": string;
|
|
153
|
+
"#veryfront/chat/ag-ui": string;
|
|
154
|
+
"#veryfront/chat/compat": string;
|
|
155
|
+
"#veryfront/chat/conversation": string;
|
|
166
156
|
"#veryfront/chat/protocol": string;
|
|
157
|
+
"#veryfront/chat/provider-errors": string;
|
|
158
|
+
"#veryfront/chat/stream-watchdog": string;
|
|
167
159
|
"#veryfront/build": string;
|
|
168
160
|
"#veryfront/cache": string;
|
|
169
161
|
"#veryfront/config": string;
|
|
@@ -270,10 +262,7 @@ declare namespace _default {
|
|
|
270
262
|
"react/jsx-runtime": string;
|
|
271
263
|
"react/jsx-dev-runtime": string;
|
|
272
264
|
"veryfront/schemas": string;
|
|
273
|
-
"veryfront/chat/conversation": string;
|
|
274
265
|
"veryfront/chat/message-prep": string;
|
|
275
|
-
"veryfront/chat/final-step-fallback": string;
|
|
276
|
-
"veryfront/chat/provider-errors": string;
|
|
277
266
|
"#dnt": string;
|
|
278
267
|
};
|
|
279
268
|
namespace compilerOptions {
|