@engenty/wizards-runtime 0.7.11 → 0.9.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +5 -1
- package/apps/runtime/dist/agents/architect.js +2 -2
- package/apps/runtime/dist/agents/studio-assistant.js +1 -1
- package/apps/runtime/dist/agents/website.js +1 -1
- package/apps/runtime/dist/app.js +1 -1
- package/apps/runtime/dist/assistant/browser.js +1 -1
- package/apps/runtime/dist/authoring/guide.js +16 -4
- package/apps/runtime/dist/authoring/ops.js +1 -1
- package/apps/runtime/dist/cli/exec.js +4 -1
- package/apps/runtime/dist/cli/features.js +1 -1
- package/apps/runtime/dist/cli/validate.js +2 -2
- package/apps/runtime/dist/connectors/builtin.js +4 -2
- package/apps/runtime/dist/connectors/external.js +1 -1
- package/apps/runtime/dist/connectors/index.js +37 -6
- package/apps/runtime/dist/connectors/smtp.js +221 -0
- package/apps/runtime/dist/connectors/via-account.js +1 -1
- package/apps/runtime/dist/credits/estimate.js +1 -1
- package/apps/runtime/dist/db/migrations/tenant/0018_share_job.sql +18 -0
- package/apps/runtime/dist/db/migrations/tenant/meta/0018_snapshot.json +3460 -0
- package/apps/runtime/dist/db/migrations/tenant/meta/_journal.json +7 -0
- package/apps/runtime/dist/db/schema.js +19 -0
- package/apps/runtime/dist/docs/dev/index.md +25 -13
- package/apps/runtime/dist/docs/dev/integration/index.md +1 -1
- package/apps/runtime/dist/docs/dev/integration/openui.md +1 -1
- package/apps/runtime/dist/docs/dev/integration/own-ui.md +2 -2
- package/apps/runtime/dist/docs/dev/meta.json +8 -2
- package/apps/runtime/dist/docs/dev/plugins/examples.md +2 -3
- package/apps/runtime/dist/docs/dev/plugins/studio.md +5 -6
- package/apps/runtime/dist/docs/dev/plugins/styling.md +10 -7
- package/apps/runtime/dist/docs/dev/wizard-format/agent-steps.md +112 -0
- package/apps/runtime/dist/docs/dev/wizard-format/branches.md +63 -0
- package/apps/runtime/dist/docs/dev/wizard-format/film-steps.md +47 -0
- package/apps/runtime/dist/docs/dev/wizard-format/generate-steps.md +57 -0
- package/apps/runtime/dist/docs/dev/wizard-format/index.md +188 -0
- package/apps/runtime/dist/docs/dev/wizard-format/lists-and-connections.md +65 -0
- package/apps/runtime/dist/docs/dev/wizard-format/meta.json +19 -0
- package/apps/runtime/dist/docs/dev/wizard-format/pages-and-fields.md +102 -0
- package/apps/runtime/dist/docs/dev/wizard-format/review-and-result.md +64 -0
- package/apps/runtime/dist/docs/dev/wizard-format/templates.md +57 -0
- package/apps/runtime/dist/docs/dev/wizard-format/validate.md +63 -0
- package/apps/runtime/dist/docs/dev/wizard-format/views-and-widgets.md +103 -0
- package/apps/runtime/dist/docs/dev/wizard-format/wizard-file.md +147 -0
- package/apps/runtime/dist/docs/user/build-a-wizard.md +2 -2
- package/apps/runtime/dist/docs/user/run.md +21 -4
- package/apps/runtime/dist/docs/user/space.md +14 -10
- package/apps/runtime/dist/docs/user/steps.md +26 -1
- package/apps/runtime/dist/docs/user/test-and-share.md +3 -2
- package/apps/runtime/dist/documents/parse.js +1 -1
- package/apps/runtime/dist/engine/decide.js +1 -1
- package/apps/runtime/dist/engine/events.js +1 -1
- package/apps/runtime/dist/engine/input.js +3 -3
- package/apps/runtime/dist/engine/prepare.js +1 -1
- package/apps/runtime/dist/engine/requirements.js +25 -10
- package/apps/runtime/dist/engine/runner.js +23 -14
- package/apps/runtime/dist/engine/steps.js +6 -6
- package/apps/runtime/dist/engine/surface.js +3 -3
- package/apps/runtime/dist/engine/template.js +4 -4
- package/apps/runtime/dist/env.js +5 -0
- package/apps/runtime/dist/features.js +1 -1
- package/apps/runtime/dist/film/project.js +2 -2
- package/apps/runtime/dist/film/step.js +2 -2
- package/apps/runtime/dist/harness/antigravity.js +2 -10
- package/apps/runtime/dist/harness/apple.js +1 -1
- package/apps/runtime/dist/harness/claude.js +3 -11
- package/apps/runtime/dist/harness/codex.js +6 -12
- package/apps/runtime/dist/harness/cursor.js +2 -10
- package/apps/runtime/dist/harness/env.js +23 -0
- package/apps/runtime/dist/harness/index.js +6 -3
- package/apps/runtime/dist/harness/model.js +1 -1
- package/apps/runtime/dist/harness/terminal.js +1 -1
- package/apps/runtime/dist/mcp/flow-app.js +1 -1
- package/apps/runtime/dist/media/conversation.js +1 -1
- package/apps/runtime/dist/media/transcribe.js +1 -1
- package/apps/runtime/dist/model-errors.js +1 -1
- package/apps/runtime/dist/models.js +3 -3
- package/apps/runtime/dist/plugin.js +1 -1
- package/apps/runtime/dist/plugins/discovery.js +1 -1
- package/apps/runtime/dist/plugins/loader.js +2 -2
- package/apps/runtime/dist/plugins/runs.js +2 -2
- package/apps/runtime/dist/render/downloads.js +2 -2
- package/apps/runtime/dist/render/shaped.js +1 -1
- package/apps/runtime/dist/routes/api.js +2 -2
- package/apps/runtime/dist/routes/lang.js +1 -1
- package/apps/runtime/dist/routes/marketplace.js +1 -1
- package/apps/runtime/dist/routes/runs.js +110 -10
- package/apps/runtime/dist/routes/studio.js +20 -8
- package/apps/runtime/dist/services/access.js +1 -1
- package/apps/runtime/dist/services/api.js +4 -4
- package/apps/runtime/dist/services/assistant-chats.js +2 -2
- package/apps/runtime/dist/services/brand.js +2 -2
- package/apps/runtime/dist/services/cloud.js +1 -1
- package/apps/runtime/dist/services/errors.js +1 -1
- package/apps/runtime/dist/services/files.js +1 -1
- package/apps/runtime/dist/services/knowledge-model.js +3 -3
- package/apps/runtime/dist/services/knowledge.js +2 -2
- package/apps/runtime/dist/services/marketplace.js +5 -5
- package/apps/runtime/dist/services/package-file.js +3 -3
- package/apps/runtime/dist/services/package.js +2 -2
- package/apps/runtime/dist/services/profile.js +1 -1
- package/apps/runtime/dist/services/project-files.js +2 -2
- package/apps/runtime/dist/services/project-index.js +1 -1
- package/apps/runtime/dist/services/projects.js +2 -2
- package/apps/runtime/dist/services/runners.js +3 -3
- package/apps/runtime/dist/services/runs.js +2 -2
- package/apps/runtime/dist/services/shares.js +1 -1
- package/apps/runtime/dist/services/site.js +2 -2
- package/apps/runtime/dist/services/space-categories.js +3 -3
- package/apps/runtime/dist/services/space-data.js +4 -4
- package/apps/runtime/dist/services/spaces.js +6 -6
- package/apps/runtime/dist/services/translations.js +5 -5
- package/apps/runtime/dist/services/widgets.js +1 -1
- package/apps/runtime/dist/services/wizards.js +3 -3
- package/apps/runtime/dist/sharing/agent.js +65 -0
- package/apps/runtime/dist/sharing/files.js +121 -0
- package/apps/runtime/dist/sharing/index.js +298 -0
- package/apps/runtime/dist/sharing/jobs.js +186 -0
- package/apps/runtime/dist/sharing/state.js +116 -0
- package/apps/runtime/dist/sharing/targets/browser.js +131 -0
- package/apps/runtime/dist/sharing/targets/connector.js +63 -0
- package/apps/runtime/dist/sharing/targets/email.js +98 -0
- package/apps/runtime/dist/sharing/targets/slack.js +54 -0
- package/apps/runtime/dist/sharing/targets/storage.js +164 -0
- package/apps/runtime/dist/sharing/types.js +13 -0
- package/apps/runtime/dist/store/index.js +3 -3
- package/apps/runtime/dist/store/shared-lists.js +1 -1
- package/apps/runtime/dist/texts.js +109 -1
- package/apps/runtime/dist/tools/browser.js +48 -0
- package/apps/runtime/dist/tools/connector.js +3 -2
- package/apps/runtime/dist/tools/index.js +1 -1
- package/apps/runtime/dist/tools/plugin.js +2 -2
- package/apps/runtime/dist/tools/project.js +1 -1
- package/apps/runtime/dist/tools/store.js +20 -3
- package/apps/runtime/dist/widgets/bundle.js +1 -1
- package/apps/runtime/dist/widgets/step.js +3 -3
- package/apps/web/dist/assets/AboutDialog-CB0EwGxP.js +1 -0
- package/apps/web/dist/assets/{ChangelogDialog-CZrd-MgC.js → ChangelogDialog-BQRBkiVD.js} +1 -1
- package/apps/web/dist/assets/ChatView-BLZkLlHi.js +10 -0
- package/apps/web/dist/assets/EditorPage-YawfYRAL.js +4 -0
- package/apps/web/dist/assets/{FlowPreview-oX_Emm7u.js → FlowPreview-B0xF8e3r.js} +1 -1
- package/apps/web/dist/assets/{Harness-DiB8CBdY.js → Harness-snRbALGd.js} +2 -2
- package/apps/web/dist/assets/NewWizardChannels-BGzp9z0m.js +1 -0
- package/apps/web/dist/assets/{NewWizardPage-C2Oh2sMM.js → NewWizardPage-cOHfwcnA.js} +2 -2
- package/apps/web/dist/assets/OnboardingPage-jB3ChKi3.js +1 -0
- package/apps/web/dist/assets/ProjectPage-hRCBquNb.js +1 -0
- package/apps/web/dist/assets/PublicRunner-D_ZkOyRV.js +1 -0
- package/apps/web/dist/assets/{ReadOnly-CD5YWvrW.js → ReadOnly-Db_odT6Y.js} +1 -1
- package/apps/web/dist/assets/RunDrawer-B6AQw90D.js +4 -0
- package/apps/web/dist/assets/RunnerView-DHUkvi_G.js +1 -0
- package/apps/web/dist/assets/SettingsPage-DleMaLz8.js +2 -0
- package/apps/web/dist/assets/SetupPage-DTvVJR1U.js +1 -0
- package/apps/web/dist/assets/{SharePage-B6u9kS12.js → SharePage-Bl-rfCji.js} +1 -1
- package/apps/web/dist/assets/changelog-BcW79fHm.js +1 -0
- package/apps/web/dist/assets/esm-Bji0ThaK.js +7 -0
- package/apps/web/dist/assets/index-CQKA-z2z.js +14 -0
- package/apps/web/dist/assets/index-Cnziz-H-.css +1 -0
- package/apps/web/dist/assets/oss-credits-BjwxLtU4.js +1 -0
- package/apps/web/dist/assets/{phone-BjmhIKuT.js → phone-DwAf8S3l.js} +1 -1
- package/apps/web/dist/assets/settings-menu-B1ND1GF3.js +1 -0
- package/apps/web/dist/assets/ui-Cdv3QUOd.js +137 -0
- package/apps/web/dist/assets/{upload-JX9BcMTq.js → upload-DCMZI6pp.js} +1 -1
- package/apps/web/dist/assets/{wrench-BSxnPs2_.js → wrench-BRYtnxti.js} +1 -1
- package/apps/web/dist/index.html +3 -3
- package/apps/web/dist/mcp/flow.html +53 -53
- package/node_modules/{@engenty-wizards/shared → @engenty/wizards-shared}/dist/definition.d.ts +22 -0
- package/node_modules/{@engenty-wizards/shared → @engenty/wizards-shared}/dist/definition.js +26 -0
- package/node_modules/{@engenty-wizards/shared → @engenty/wizards-shared}/dist/run.d.ts +6 -0
- package/node_modules/{@engenty-wizards/shared → @engenty/wizards-shared}/dist/run.js +2 -0
- package/node_modules/@engenty/wizards-shared/dist/sharing.d.ts +135 -0
- package/node_modules/@engenty/wizards-shared/dist/sharing.js +11 -0
- package/node_modules/{@engenty-wizards/shared → @engenty/wizards-shared}/dist/store.d.ts +8 -0
- package/node_modules/{@engenty-wizards/shared → @engenty/wizards-shared}/package.json +1 -1
- package/package.json +4 -3
- package/plugin/.claude-plugin/plugin.json +1 -1
- package/apps/runtime/dist/docs/dev/wizard-format.md +0 -125
- package/apps/web/dist/assets/AboutDialog-CPRAlD2H.js +0 -1
- package/apps/web/dist/assets/ChatView-DVeERQdi.js +0 -10
- package/apps/web/dist/assets/EditorPage-DhUoKcss.js +0 -4
- package/apps/web/dist/assets/NewWizardChannels-Ba3XGpro.js +0 -1
- package/apps/web/dist/assets/OnboardingPage-D_4ShWrL.js +0 -1
- package/apps/web/dist/assets/ProjectPage-C58rq50b.js +0 -1
- package/apps/web/dist/assets/PublicRunner-CE0ohzyd.js +0 -1
- package/apps/web/dist/assets/RunDrawer-Cd8-qHV7.js +0 -4
- package/apps/web/dist/assets/RunnerView-BW1h4x63.js +0 -1
- package/apps/web/dist/assets/SettingsPage-ChYLv-MG.js +0 -1
- package/apps/web/dist/assets/SetupPage-hX7deF0g.js +0 -1
- package/apps/web/dist/assets/changelog-Dn9LT4MS.js +0 -1
- package/apps/web/dist/assets/esm-C-ZWeuhU.js +0 -7
- package/apps/web/dist/assets/index-4jH2mkp7.js +0 -14
- package/apps/web/dist/assets/index-BwW652E-.css +0 -1
- package/apps/web/dist/assets/oss-credits-Bc05Qv5H.js +0 -1
- package/apps/web/dist/assets/settings-menu-Dpkp3f56.js +0 -1
- package/apps/web/dist/assets/ui-DMmT-cSO.js +0 -137
- /package/node_modules/{@engenty-wizards/shared → @engenty/wizards-shared}/dist/assistant.d.ts +0 -0
- /package/node_modules/{@engenty-wizards/shared → @engenty/wizards-shared}/dist/assistant.js +0 -0
- /package/node_modules/{@engenty-wizards/shared → @engenty/wizards-shared}/dist/changes.d.ts +0 -0
- /package/node_modules/{@engenty-wizards/shared → @engenty/wizards-shared}/dist/changes.js +0 -0
- /package/node_modules/{@engenty-wizards/shared → @engenty/wizards-shared}/dist/claude-plugin.d.ts +0 -0
- /package/node_modules/{@engenty-wizards/shared → @engenty/wizards-shared}/dist/claude-plugin.js +0 -0
- /package/node_modules/{@engenty-wizards/shared → @engenty/wizards-shared}/dist/connectors.d.ts +0 -0
- /package/node_modules/{@engenty-wizards/shared → @engenty/wizards-shared}/dist/connectors.js +0 -0
- /package/node_modules/{@engenty-wizards/shared → @engenty/wizards-shared}/dist/decision.d.ts +0 -0
- /package/node_modules/{@engenty-wizards/shared → @engenty/wizards-shared}/dist/decision.js +0 -0
- /package/node_modules/{@engenty-wizards/shared → @engenty/wizards-shared}/dist/engenty/data-tables/columns-format.d.ts +0 -0
- /package/node_modules/{@engenty-wizards/shared → @engenty/wizards-shared}/dist/engenty/data-tables/columns-format.js +0 -0
- /package/node_modules/{@engenty-wizards/shared → @engenty/wizards-shared}/dist/engenty/data-tables/columns-value.d.ts +0 -0
- /package/node_modules/{@engenty-wizards/shared → @engenty/wizards-shared}/dist/engenty/data-tables/columns-value.js +0 -0
- /package/node_modules/{@engenty-wizards/shared → @engenty/wizards-shared}/dist/engenty/data-tables/columns.d.ts +0 -0
- /package/node_modules/{@engenty-wizards/shared → @engenty/wizards-shared}/dist/engenty/data-tables/columns.js +0 -0
- /package/node_modules/{@engenty-wizards/shared → @engenty/wizards-shared}/dist/engenty/data-tables/index.d.ts +0 -0
- /package/node_modules/{@engenty-wizards/shared → @engenty/wizards-shared}/dist/engenty/data-tables/index.js +0 -0
- /package/node_modules/{@engenty-wizards/shared → @engenty/wizards-shared}/dist/engenty/search-index/catalog-lexical.d.ts +0 -0
- /package/node_modules/{@engenty-wizards/shared → @engenty/wizards-shared}/dist/engenty/search-index/catalog-lexical.js +0 -0
- /package/node_modules/{@engenty-wizards/shared → @engenty/wizards-shared}/dist/engenty/search-index/catalog-rank.d.ts +0 -0
- /package/node_modules/{@engenty-wizards/shared → @engenty/wizards-shared}/dist/engenty/search-index/catalog-rank.js +0 -0
- /package/node_modules/{@engenty-wizards/shared → @engenty/wizards-shared}/dist/feature-flags.d.ts +0 -0
- /package/node_modules/{@engenty-wizards/shared → @engenty/wizards-shared}/dist/feature-flags.js +0 -0
- /package/node_modules/{@engenty-wizards/shared → @engenty/wizards-shared}/dist/features.d.ts +0 -0
- /package/node_modules/{@engenty-wizards/shared → @engenty/wizards-shared}/dist/features.js +0 -0
- /package/node_modules/{@engenty-wizards/shared → @engenty/wizards-shared}/dist/fit.d.ts +0 -0
- /package/node_modules/{@engenty-wizards/shared → @engenty/wizards-shared}/dist/fit.js +0 -0
- /package/node_modules/{@engenty-wizards/shared → @engenty/wizards-shared}/dist/json-schema.d.ts +0 -0
- /package/node_modules/{@engenty-wizards/shared → @engenty/wizards-shared}/dist/json-schema.js +0 -0
- /package/node_modules/{@engenty-wizards/shared → @engenty/wizards-shared}/dist/knowledge.d.ts +0 -0
- /package/node_modules/{@engenty-wizards/shared → @engenty/wizards-shared}/dist/knowledge.js +0 -0
- /package/node_modules/{@engenty-wizards/shared → @engenty/wizards-shared}/dist/marketplace-entry.d.ts +0 -0
- /package/node_modules/{@engenty-wizards/shared → @engenty/wizards-shared}/dist/marketplace-entry.js +0 -0
- /package/node_modules/{@engenty-wizards/shared → @engenty/wizards-shared}/dist/marketplace-search.d.ts +0 -0
- /package/node_modules/{@engenty-wizards/shared → @engenty/wizards-shared}/dist/marketplace-search.js +0 -0
- /package/node_modules/{@engenty-wizards/shared → @engenty/wizards-shared}/dist/marketplace.d.ts +0 -0
- /package/node_modules/{@engenty-wizards/shared → @engenty/wizards-shared}/dist/marketplace.js +0 -0
- /package/node_modules/{@engenty-wizards/shared → @engenty/wizards-shared}/dist/merge.d.ts +0 -0
- /package/node_modules/{@engenty-wizards/shared → @engenty/wizards-shared}/dist/merge.js +0 -0
- /package/node_modules/{@engenty-wizards/shared → @engenty/wizards-shared}/dist/numbers.d.ts +0 -0
- /package/node_modules/{@engenty-wizards/shared → @engenty/wizards-shared}/dist/numbers.js +0 -0
- /package/node_modules/{@engenty-wizards/shared → @engenty/wizards-shared}/dist/projects.d.ts +0 -0
- /package/node_modules/{@engenty-wizards/shared → @engenty/wizards-shared}/dist/projects.js +0 -0
- /package/node_modules/{@engenty-wizards/shared → @engenty/wizards-shared}/dist/providers.d.ts +0 -0
- /package/node_modules/{@engenty-wizards/shared → @engenty/wizards-shared}/dist/providers.js +0 -0
- /package/node_modules/{@engenty-wizards/shared → @engenty/wizards-shared}/dist/runner-texts.d.ts +0 -0
- /package/node_modules/{@engenty-wizards/shared → @engenty/wizards-shared}/dist/runner-texts.js +0 -0
- /package/node_modules/{@engenty-wizards/shared → @engenty/wizards-shared}/dist/runners.d.ts +0 -0
- /package/node_modules/{@engenty-wizards/shared → @engenty/wizards-shared}/dist/runners.js +0 -0
- /package/node_modules/{@engenty-wizards/shared → @engenty/wizards-shared}/dist/shape.d.ts +0 -0
- /package/node_modules/{@engenty-wizards/shared → @engenty/wizards-shared}/dist/shape.js +0 -0
- /package/node_modules/{@engenty-wizards/shared → @engenty/wizards-shared}/dist/space-data.d.ts +0 -0
- /package/node_modules/{@engenty-wizards/shared → @engenty/wizards-shared}/dist/space-data.js +0 -0
- /package/node_modules/{@engenty-wizards/shared → @engenty/wizards-shared}/dist/store.js +0 -0
- /package/node_modules/{@engenty-wizards/shared → @engenty/wizards-shared}/dist/surface.d.ts +0 -0
- /package/node_modules/{@engenty-wizards/shared → @engenty/wizards-shared}/dist/surface.js +0 -0
- /package/node_modules/{@engenty-wizards/shared → @engenty/wizards-shared}/dist/texts.d.ts +0 -0
- /package/node_modules/{@engenty-wizards/shared → @engenty/wizards-shared}/dist/texts.js +0 -0
- /package/node_modules/{@engenty-wizards/shared → @engenty/wizards-shared}/dist/translation.d.ts +0 -0
- /package/node_modules/{@engenty-wizards/shared → @engenty/wizards-shared}/dist/translation.js +0 -0
- /package/node_modules/{@engenty-wizards/shared → @engenty/wizards-shared}/dist/wizard-package.d.ts +0 -0
- /package/node_modules/{@engenty-wizards/shared → @engenty/wizards-shared}/dist/wizard-package.js +0 -0
- /package/node_modules/{@engenty-wizards/shared → @engenty/wizards-shared}/dist/workspace.d.ts +0 -0
- /package/node_modules/{@engenty-wizards/shared → @engenty/wizards-shared}/dist/workspace.js +0 -0
|
@@ -0,0 +1,131 @@
|
|
|
1
|
+
import { existsSync } from "node:fs";
|
|
2
|
+
import { createTool } from "@mastra/core/tools";
|
|
3
|
+
import { z } from "zod";
|
|
4
|
+
import { env } from "../../env.js";
|
|
5
|
+
import { browserTools } from "../../tools/browser.js";
|
|
6
|
+
import { runShareAgent } from "../agent.js";
|
|
7
|
+
import { line } from "../types.js";
|
|
8
|
+
const RULES = [
|
|
9
|
+
"You work in a browser the person watches. Open the address first, then find the place the request names.",
|
|
10
|
+
"Sign-ins are the person's: on a login form call browser_request_credentials, for anything else in the way (a captcha, 'continue with Google', a cookie wall) browser_request_user. Never type a password or a code yourself. When the person skips a sign-in, stop and report it.",
|
|
11
|
+
"Attach files with browser_upload.",
|
|
12
|
+
"Right before the step that makes it public or sends it (Post, Publish, Send, Submit), call confirm_publish with where and exactly what goes out. Go on only when it answers allowed. If the person asked for a draft, save the draft and publish nothing.",
|
|
13
|
+
"Afterwards look for the address of what you published and report it.",
|
|
14
|
+
].join("\n");
|
|
15
|
+
/** Leave to publish: the person sees where and what, and says yes or no. */
|
|
16
|
+
function confirmTool(step, site) {
|
|
17
|
+
return createTool({
|
|
18
|
+
id: "confirm_publish",
|
|
19
|
+
description: "Ask the person for leave right before the step that makes the result public or sends it. They see where and what. WAITS for the person.",
|
|
20
|
+
inputSchema: z.object({
|
|
21
|
+
where: z
|
|
22
|
+
.string()
|
|
23
|
+
.min(1)
|
|
24
|
+
.max(200)
|
|
25
|
+
.describe("One sentence in the person's language: the site and the place it goes to."),
|
|
26
|
+
what: z.string().min(1).describe("The exact text and the files that will go out."),
|
|
27
|
+
}),
|
|
28
|
+
execute: async ({ where, what }) => {
|
|
29
|
+
const answer = await step.ask({
|
|
30
|
+
kind: "confirm",
|
|
31
|
+
reason: where,
|
|
32
|
+
service: site,
|
|
33
|
+
action: { id: "publish", summary: where, destructive: false },
|
|
34
|
+
input: what.slice(0, 4000),
|
|
35
|
+
once: true,
|
|
36
|
+
});
|
|
37
|
+
if (!answer) {
|
|
38
|
+
return {
|
|
39
|
+
allowed: false,
|
|
40
|
+
note: "Nobody is there to allow it. Publish nothing and report it.",
|
|
41
|
+
};
|
|
42
|
+
}
|
|
43
|
+
return answer.type === "done"
|
|
44
|
+
? { allowed: true }
|
|
45
|
+
: {
|
|
46
|
+
allowed: false,
|
|
47
|
+
note: "The person did not allow it. Publish nothing; report that it was not published.",
|
|
48
|
+
};
|
|
49
|
+
},
|
|
50
|
+
});
|
|
51
|
+
}
|
|
52
|
+
const POST = line("sharePostInstructions");
|
|
53
|
+
/**
|
|
54
|
+
* Free style: the wizard opens a website in its browser, the person signs in themselves, and an
|
|
55
|
+
* agent posts the result there the way the person describes it. For every place that has no
|
|
56
|
+
* connector.
|
|
57
|
+
*/
|
|
58
|
+
export const browserTarget = {
|
|
59
|
+
id: "browser",
|
|
60
|
+
name: "Website",
|
|
61
|
+
description: line("shareWebsiteDescription"),
|
|
62
|
+
icon: "globe",
|
|
63
|
+
takes: "files",
|
|
64
|
+
agent: true,
|
|
65
|
+
available: () => Boolean(env.browserCdpUrl) || existsSync(env.chromePath),
|
|
66
|
+
fields: [
|
|
67
|
+
{
|
|
68
|
+
id: "url",
|
|
69
|
+
kind: "url",
|
|
70
|
+
required: true,
|
|
71
|
+
label: line("shareWebsiteAddress"),
|
|
72
|
+
placeholder: "https://…",
|
|
73
|
+
},
|
|
74
|
+
{
|
|
75
|
+
id: "instructions",
|
|
76
|
+
kind: "textarea",
|
|
77
|
+
required: true,
|
|
78
|
+
label: line("shareWhatThere"),
|
|
79
|
+
placeholder: line("shareWhatTherePlaceholder"),
|
|
80
|
+
},
|
|
81
|
+
],
|
|
82
|
+
presets: [
|
|
83
|
+
{
|
|
84
|
+
label: "LinkedIn",
|
|
85
|
+
values: {
|
|
86
|
+
url: "https://www.linkedin.com/feed/",
|
|
87
|
+
instructions: POST,
|
|
88
|
+
},
|
|
89
|
+
},
|
|
90
|
+
{
|
|
91
|
+
label: "X",
|
|
92
|
+
values: {
|
|
93
|
+
url: "https://x.com/compose/post",
|
|
94
|
+
instructions: POST,
|
|
95
|
+
},
|
|
96
|
+
},
|
|
97
|
+
{
|
|
98
|
+
label: "Facebook",
|
|
99
|
+
values: {
|
|
100
|
+
url: "https://www.facebook.com/",
|
|
101
|
+
instructions: POST,
|
|
102
|
+
},
|
|
103
|
+
},
|
|
104
|
+
{
|
|
105
|
+
label: "Instagram",
|
|
106
|
+
values: {
|
|
107
|
+
url: "https://www.instagram.com/",
|
|
108
|
+
instructions: POST,
|
|
109
|
+
},
|
|
110
|
+
},
|
|
111
|
+
{
|
|
112
|
+
label: "Meta Ads",
|
|
113
|
+
values: {
|
|
114
|
+
url: "https://adsmanager.facebook.com/",
|
|
115
|
+
instructions: line("shareAdInstructions"),
|
|
116
|
+
},
|
|
117
|
+
},
|
|
118
|
+
],
|
|
119
|
+
send(input, ctx) {
|
|
120
|
+
const url = input.values.url.trim();
|
|
121
|
+
return runShareAgent(ctx, input, {
|
|
122
|
+
task: `On ${url}: ${input.values.instructions}`,
|
|
123
|
+
rules: RULES,
|
|
124
|
+
maxSteps: 60,
|
|
125
|
+
tools: (step, files) => ({
|
|
126
|
+
...browserTools(step, [], files),
|
|
127
|
+
confirm_publish: confirmTool(step, new URL(url).hostname),
|
|
128
|
+
}),
|
|
129
|
+
});
|
|
130
|
+
},
|
|
131
|
+
};
|
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
import { refreshConnector, resolveConnector } from "../../connectors/external.js";
|
|
2
|
+
import { toolsOf } from "../../tools/connector.js";
|
|
3
|
+
import { runShareAgent } from "../agent.js";
|
|
4
|
+
import { line, ShareError } from "../types.js";
|
|
5
|
+
const RULES = [
|
|
6
|
+
"You work in the person's account through the service's actions. Look first (search, list) where the result belongs, then write.",
|
|
7
|
+
"Actions that change the account ask the person before they run. When they say no, stop and report it — do not try another way.",
|
|
8
|
+
"To send a file, give `file:<name>` with the file's name as listed, where an action's input asks for `*_base64`.",
|
|
9
|
+
].join("\n");
|
|
10
|
+
/**
|
|
11
|
+
* The result through any connector that can write: one the product ships, or one the project
|
|
12
|
+
* imported from the integrations registry (Notion, WordPress, an ads account …). The person
|
|
13
|
+
* says in their words what should happen; an agent does it with the connector's actions.
|
|
14
|
+
*/
|
|
15
|
+
export function connectorTarget(connector, imported) {
|
|
16
|
+
const id = `connector:${connector.id}`;
|
|
17
|
+
const connection = (actions) => ({
|
|
18
|
+
id: `share:${id}`,
|
|
19
|
+
connector: connector.id,
|
|
20
|
+
actions,
|
|
21
|
+
title: connector.name,
|
|
22
|
+
});
|
|
23
|
+
return {
|
|
24
|
+
id,
|
|
25
|
+
name: connector.name,
|
|
26
|
+
description: line("shareConnectorDescription"),
|
|
27
|
+
icon: "plug",
|
|
28
|
+
takes: "files",
|
|
29
|
+
agent: true,
|
|
30
|
+
// What the project imported it wants to use; what ships with the product is named by the wizard.
|
|
31
|
+
byDefault: imported,
|
|
32
|
+
connection: () => connection(connector.actions.map((a) => a.id)),
|
|
33
|
+
fields: [
|
|
34
|
+
{
|
|
35
|
+
id: "instructions",
|
|
36
|
+
kind: "textarea",
|
|
37
|
+
required: true,
|
|
38
|
+
label: line("shareWhatHappens"),
|
|
39
|
+
placeholder: line("shareWhatHappensPlaceholder"),
|
|
40
|
+
},
|
|
41
|
+
],
|
|
42
|
+
async send(input, ctx) {
|
|
43
|
+
const account = await ctx.account();
|
|
44
|
+
if (!account) {
|
|
45
|
+
throw new ShareError(line("shareConnectFirst", { name: connector.name }));
|
|
46
|
+
}
|
|
47
|
+
// An MCP server that lists its tools only to a signed-in account: ask it now.
|
|
48
|
+
if (imported && !connector.actions.length) {
|
|
49
|
+
await refreshConnector(ctx.projectId, connector.id, account.ctx.accessToken).catch(() => undefined);
|
|
50
|
+
}
|
|
51
|
+
const current = (await resolveConnector(ctx.projectId, connector.id))?.connector ?? connector;
|
|
52
|
+
return runShareAgent(ctx, input, {
|
|
53
|
+
task: `In ${connector.name}: ${input.values.instructions}`,
|
|
54
|
+
rules: RULES,
|
|
55
|
+
tools: (step, files) => toolsOf(connection(current.actions.map((a) => a.id)), step, files),
|
|
56
|
+
});
|
|
57
|
+
},
|
|
58
|
+
};
|
|
59
|
+
}
|
|
60
|
+
/** A connector can carry a result somewhere when it has an action that writes. */
|
|
61
|
+
export function canPublish(connector) {
|
|
62
|
+
return !connector.actions.length || connector.actions.some((a) => a.group !== "read");
|
|
63
|
+
}
|
|
@@ -0,0 +1,98 @@
|
|
|
1
|
+
import { MAIL_SENDERS, MailTooLarge, sendAsAccount, sendFromServer, serverSender, } from "../../connectors/smtp.js";
|
|
2
|
+
import { megabytes } from "../files.js";
|
|
3
|
+
import { line, ShareError } from "../types.js";
|
|
4
|
+
/** What mail providers take in one message, with room for the encoding. */
|
|
5
|
+
const ATTACH_LIMIT = 18_000_000;
|
|
6
|
+
/** From the server's own address, people without an account send this much per run. */
|
|
7
|
+
const VISITOR_MAILS = 3;
|
|
8
|
+
const VISITOR_RECIPIENTS = 3;
|
|
9
|
+
const RECIPIENTS = 20;
|
|
10
|
+
const ADDRESS = /^[^\s@<>,;]+@[^\s@<>,;]+\.[^\s@<>,;]+$/;
|
|
11
|
+
/** "a@b.at, c@d.at; e@f.at" → the addresses; throws on one that is none. */
|
|
12
|
+
export function parseRecipients(raw) {
|
|
13
|
+
const list = raw
|
|
14
|
+
.split(/[\s,;]+/)
|
|
15
|
+
.map((a) => a.trim())
|
|
16
|
+
.filter(Boolean);
|
|
17
|
+
const bad = list.find((a) => !ADDRESS.test(a));
|
|
18
|
+
if (bad) {
|
|
19
|
+
throw new ShareError(line("shareNotAnAddress", { address: bad }));
|
|
20
|
+
}
|
|
21
|
+
return [...new Set(list)];
|
|
22
|
+
}
|
|
23
|
+
/**
|
|
24
|
+
* The result by e-mail: the picked files as attachments. Sent from the mailbox the person
|
|
25
|
+
* connects (Gmail, Outlook, or any mailbox over SMTP) — or, where the server has an address of
|
|
26
|
+
* its own, without any account.
|
|
27
|
+
*/
|
|
28
|
+
export const emailTarget = {
|
|
29
|
+
id: "email",
|
|
30
|
+
name: line("shareEmailName"),
|
|
31
|
+
description: line("shareEmailDescription"),
|
|
32
|
+
icon: "mail",
|
|
33
|
+
takes: "files",
|
|
34
|
+
connection: () => serverSender()
|
|
35
|
+
? null
|
|
36
|
+
: {
|
|
37
|
+
id: "share:email",
|
|
38
|
+
target: { connectors: [...MAIL_SENDERS] },
|
|
39
|
+
actions: ["send_message"],
|
|
40
|
+
title: "E-Mail",
|
|
41
|
+
},
|
|
42
|
+
fields: [
|
|
43
|
+
{
|
|
44
|
+
id: "to",
|
|
45
|
+
kind: "emails",
|
|
46
|
+
required: true,
|
|
47
|
+
label: line("shareTo"),
|
|
48
|
+
placeholder: line("shareToPlaceholder"),
|
|
49
|
+
},
|
|
50
|
+
{ id: "subject", kind: "text", required: true, label: line("shareSubject") },
|
|
51
|
+
{ id: "message", kind: "textarea", label: line("shareMessage") },
|
|
52
|
+
],
|
|
53
|
+
defaults: (scope) => ({ subject: scope.title, message: scope.message ?? "" }),
|
|
54
|
+
async send({ values, files }, ctx) {
|
|
55
|
+
const to = parseRecipients(values.to ?? "");
|
|
56
|
+
if (!to.length) {
|
|
57
|
+
throw new ShareError(line("shareNoAddress"));
|
|
58
|
+
}
|
|
59
|
+
if (to.length > RECIPIENTS) {
|
|
60
|
+
throw new ShareError(line("shareTooManyAddresses", { max: RECIPIENTS }));
|
|
61
|
+
}
|
|
62
|
+
const size = files.reduce((n, f) => n + f.data.byteLength, 0);
|
|
63
|
+
if (size > ATTACH_LIMIT) {
|
|
64
|
+
throw new ShareError(line("shareAttachmentsTooLarge", { size: megabytes(size) }));
|
|
65
|
+
}
|
|
66
|
+
const message = {
|
|
67
|
+
to,
|
|
68
|
+
subject: (values.subject ?? "").trim() || ctx.title,
|
|
69
|
+
text: (values.message ?? "").trim(),
|
|
70
|
+
attachments: files.map((f) => ({ name: f.name, mime: f.mime, data: f.data })),
|
|
71
|
+
};
|
|
72
|
+
const account = await ctx.account();
|
|
73
|
+
if (account) {
|
|
74
|
+
await ctx.emit(line("shareSendingFrom", { account: account.label }));
|
|
75
|
+
try {
|
|
76
|
+
await sendAsAccount(account.connector, account.ctx, message);
|
|
77
|
+
}
|
|
78
|
+
catch (err) {
|
|
79
|
+
if (err instanceof MailTooLarge) {
|
|
80
|
+
throw new ShareError(line("shareMailboxLimit"));
|
|
81
|
+
}
|
|
82
|
+
throw err;
|
|
83
|
+
}
|
|
84
|
+
}
|
|
85
|
+
else if (serverSender()) {
|
|
86
|
+
// The server's address is everyone's to send from: a visitor gets a few mails per run.
|
|
87
|
+
if (!ctx.member && (ctx.sentBefore >= VISITOR_MAILS || to.length > VISITOR_RECIPIENTS)) {
|
|
88
|
+
throw new ShareError(line("shareVisitorMails", { mails: VISITOR_MAILS, recipients: VISITOR_RECIPIENTS }));
|
|
89
|
+
}
|
|
90
|
+
await ctx.emit(line("shareSendingFrom", { account: serverSender() ?? "" }));
|
|
91
|
+
await sendFromServer(message);
|
|
92
|
+
}
|
|
93
|
+
else {
|
|
94
|
+
throw new ShareError(line("shareConnectMailbox"));
|
|
95
|
+
}
|
|
96
|
+
return { summary: line("shareSentTo", { to: to.join(", ") }) };
|
|
97
|
+
},
|
|
98
|
+
};
|
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
import { line, ShareError } from "../types.js";
|
|
2
|
+
/**
|
|
3
|
+
* A message in a Slack channel, as the person: their text and the link to the result page.
|
|
4
|
+
* The connector posts text only, so files travel as the link.
|
|
5
|
+
*/
|
|
6
|
+
export const slackTarget = {
|
|
7
|
+
id: "slack",
|
|
8
|
+
name: "Slack",
|
|
9
|
+
description: line("shareSlackDescription"),
|
|
10
|
+
icon: "chat",
|
|
11
|
+
takes: "link",
|
|
12
|
+
connection: () => ({
|
|
13
|
+
id: "share:slack",
|
|
14
|
+
target: { connectors: ["slack"] },
|
|
15
|
+
actions: ["list_channels", "post_message"],
|
|
16
|
+
title: "Slack",
|
|
17
|
+
}),
|
|
18
|
+
fields: [
|
|
19
|
+
{
|
|
20
|
+
id: "channel",
|
|
21
|
+
kind: "select",
|
|
22
|
+
required: true,
|
|
23
|
+
fromAccount: true,
|
|
24
|
+
label: line("shareChannel"),
|
|
25
|
+
},
|
|
26
|
+
{ id: "message", kind: "textarea", required: true, label: line("shareMessage") },
|
|
27
|
+
],
|
|
28
|
+
defaults: (scope) => ({
|
|
29
|
+
message: [scope.title, scope.text?.slice(0, 2500)].filter(Boolean).join("\n\n"),
|
|
30
|
+
}),
|
|
31
|
+
async choices(field, ctx) {
|
|
32
|
+
const account = field === "channel" ? await ctx.account() : null;
|
|
33
|
+
const list = account?.connector.actions.find((a) => a.id === "list_channels");
|
|
34
|
+
if (!account || !list) {
|
|
35
|
+
return [];
|
|
36
|
+
}
|
|
37
|
+
const { channels } = (await list.handler({ limit: 50 }, account.ctx));
|
|
38
|
+
return channels
|
|
39
|
+
.sort((a, b) => a.name.localeCompare(b.name))
|
|
40
|
+
.map((c) => ({ value: c.id, label: `${c.is_private ? "🔒 " : "#"}${c.name}` }));
|
|
41
|
+
},
|
|
42
|
+
async send({ values }, ctx) {
|
|
43
|
+
const account = await ctx.account();
|
|
44
|
+
const post = account?.connector.actions.find((a) => a.id === "post_message");
|
|
45
|
+
if (!account || !post) {
|
|
46
|
+
throw new ShareError(line("shareConnectFirst", { name: "Slack" }));
|
|
47
|
+
}
|
|
48
|
+
const link = await ctx.link();
|
|
49
|
+
const text = [values.message?.trim(), link].filter(Boolean).join("\n\n");
|
|
50
|
+
await ctx.emit(line("sharePostingAs", { account: account.label }));
|
|
51
|
+
await post.handler({ channel: values.channel, text }, account.ctx);
|
|
52
|
+
return { summary: line("sharePostedSlack") };
|
|
53
|
+
},
|
|
54
|
+
};
|
|
@@ -0,0 +1,164 @@
|
|
|
1
|
+
import { BUILTIN_CONNECTORS } from "../../connectors/builtin.js";
|
|
2
|
+
import { line, ShareError } from "../types.js";
|
|
3
|
+
async function call(ctx, url, init = {}) {
|
|
4
|
+
const res = await ctx.fetchImpl(url, {
|
|
5
|
+
...init,
|
|
6
|
+
headers: { Authorization: `Bearer ${ctx.accessToken}`, ...init.headers },
|
|
7
|
+
});
|
|
8
|
+
if (!res.ok) {
|
|
9
|
+
throw new Error(`${new URL(url).hostname} (${res.status}): ${(await res.text()).slice(0, 200)}`);
|
|
10
|
+
}
|
|
11
|
+
return res;
|
|
12
|
+
}
|
|
13
|
+
const segments = (folder) => folder
|
|
14
|
+
.split("/")
|
|
15
|
+
.map((s) => s.trim())
|
|
16
|
+
.filter(Boolean);
|
|
17
|
+
// --- Google Drive -----------------------------------------------------------------
|
|
18
|
+
// With the scope `drive.file` the wizard sees only what it created itself: its folders are
|
|
19
|
+
// found again by name, and nothing else in the Drive is readable.
|
|
20
|
+
const DRIVE = "https://www.googleapis.com/drive/v3/files";
|
|
21
|
+
const DRIVE_FOLDER = "application/vnd.google-apps.folder";
|
|
22
|
+
async function driveFolder(ctx, name, parent) {
|
|
23
|
+
const q = [
|
|
24
|
+
`name = '${name.replace(/['\\]/g, "\\$&")}'`,
|
|
25
|
+
`mimeType = '${DRIVE_FOLDER}'`,
|
|
26
|
+
"trashed = false",
|
|
27
|
+
`'${parent ?? "root"}' in parents`,
|
|
28
|
+
].join(" and ");
|
|
29
|
+
const found = (await (await call(ctx, `${DRIVE}?q=${encodeURIComponent(q)}&fields=files(id)&pageSize=1`)).json());
|
|
30
|
+
if (found.files?.[0]) {
|
|
31
|
+
return found.files[0].id;
|
|
32
|
+
}
|
|
33
|
+
const created = (await (await call(ctx, `${DRIVE}?fields=id`, {
|
|
34
|
+
method: "POST",
|
|
35
|
+
headers: { "Content-Type": "application/json" },
|
|
36
|
+
body: JSON.stringify({
|
|
37
|
+
name,
|
|
38
|
+
mimeType: DRIVE_FOLDER,
|
|
39
|
+
...(parent ? { parents: [parent] } : {}),
|
|
40
|
+
}),
|
|
41
|
+
})).json());
|
|
42
|
+
return created.id;
|
|
43
|
+
}
|
|
44
|
+
const driveUpload = async (ctx, folder, files, each) => {
|
|
45
|
+
let parent = null;
|
|
46
|
+
for (const name of segments(folder)) {
|
|
47
|
+
parent = await driveFolder(ctx, name, parent);
|
|
48
|
+
}
|
|
49
|
+
let link = parent ? `https://drive.google.com/drive/folders/${parent}` : null;
|
|
50
|
+
for (const file of files) {
|
|
51
|
+
await each(file);
|
|
52
|
+
const boundary = `wz${Date.now().toString(36)}${Math.random().toString(36).slice(2)}`;
|
|
53
|
+
const meta = JSON.stringify({ name: file.name, ...(parent ? { parents: [parent] } : {}) });
|
|
54
|
+
const body = Buffer.concat([
|
|
55
|
+
Buffer.from(`--${boundary}\r\nContent-Type: application/json; charset=UTF-8\r\n\r\n${meta}\r\n--${boundary}\r\nContent-Type: ${file.mime}\r\n\r\n`),
|
|
56
|
+
Buffer.from(file.data),
|
|
57
|
+
Buffer.from(`\r\n--${boundary}--`),
|
|
58
|
+
]);
|
|
59
|
+
const made = (await (await call(ctx, "https://www.googleapis.com/upload/drive/v3/files?uploadType=multipart&fields=id,webViewLink", {
|
|
60
|
+
method: "POST",
|
|
61
|
+
headers: { "Content-Type": `multipart/related; boundary=${boundary}` },
|
|
62
|
+
body: new Uint8Array(body),
|
|
63
|
+
})).json());
|
|
64
|
+
link ??= made.webViewLink ?? null;
|
|
65
|
+
}
|
|
66
|
+
return link;
|
|
67
|
+
};
|
|
68
|
+
// --- OneDrive -----------------------------------------------------------------------
|
|
69
|
+
const GRAPH = "https://graph.microsoft.com/v1.0/me/drive";
|
|
70
|
+
const oneDriveUpload = async (ctx, folder, files, each) => {
|
|
71
|
+
const path = segments(folder).map(encodeURIComponent).join("/");
|
|
72
|
+
let link = null;
|
|
73
|
+
for (const file of files) {
|
|
74
|
+
await each(file);
|
|
75
|
+
const at = [path, encodeURIComponent(file.name)].filter(Boolean).join("/");
|
|
76
|
+
const item = (await (await call(ctx, `${GRAPH}/root:/${at}:/content`, {
|
|
77
|
+
method: "PUT",
|
|
78
|
+
headers: { "Content-Type": file.mime },
|
|
79
|
+
body: new Uint8Array(file.data),
|
|
80
|
+
})).json());
|
|
81
|
+
link ??= item.webUrl ?? null;
|
|
82
|
+
}
|
|
83
|
+
if (path) {
|
|
84
|
+
const dir = await call(ctx, `${GRAPH}/root:/${path}`).then((res) => res.json(), () => null);
|
|
85
|
+
link = dir?.webUrl ?? link;
|
|
86
|
+
}
|
|
87
|
+
return link;
|
|
88
|
+
};
|
|
89
|
+
const UPLOADS = {
|
|
90
|
+
"google-drive": driveUpload,
|
|
91
|
+
"microsoft-onedrive": oneDriveUpload,
|
|
92
|
+
};
|
|
93
|
+
/** The action whose provider rights an upload needs, per connector. */
|
|
94
|
+
const UPLOAD_ACTION = {
|
|
95
|
+
"google-drive": "create_file",
|
|
96
|
+
"microsoft-onedrive": "upload_file",
|
|
97
|
+
};
|
|
98
|
+
/** A connector that says itself how files are written into it (S3). */
|
|
99
|
+
function capabilityUpload(connector) {
|
|
100
|
+
const storage = connector.storage;
|
|
101
|
+
if (!storage) {
|
|
102
|
+
return null;
|
|
103
|
+
}
|
|
104
|
+
return async (ctx, folder, files, each) => {
|
|
105
|
+
let link = null;
|
|
106
|
+
for (const file of files) {
|
|
107
|
+
await each(file);
|
|
108
|
+
const entry = await storage.write(ctx, {
|
|
109
|
+
folder_ref: segments(folder).join("/") || null,
|
|
110
|
+
name: file.name,
|
|
111
|
+
mime_type: file.mime,
|
|
112
|
+
content_base64: Buffer.from(file.data).toString("base64"),
|
|
113
|
+
});
|
|
114
|
+
link ??= entry.web_url ?? null;
|
|
115
|
+
}
|
|
116
|
+
return link;
|
|
117
|
+
};
|
|
118
|
+
}
|
|
119
|
+
/** The connectors files can be saved into: `storage:<id>` is a share target for each. */
|
|
120
|
+
export function storageConnectors() {
|
|
121
|
+
return BUILTIN_CONNECTORS.filter((c) => UPLOADS[c.id] || c.storage);
|
|
122
|
+
}
|
|
123
|
+
/** The picked files, saved into a folder of the person's cloud storage. */
|
|
124
|
+
export function storageTarget(connector) {
|
|
125
|
+
const upload = UPLOADS[connector.id] ?? capabilityUpload(connector);
|
|
126
|
+
if (!upload) {
|
|
127
|
+
return null;
|
|
128
|
+
}
|
|
129
|
+
const id = `storage:${connector.id}`;
|
|
130
|
+
return {
|
|
131
|
+
id,
|
|
132
|
+
name: connector.name,
|
|
133
|
+
description: line("shareStorageDescription"),
|
|
134
|
+
icon: "folder",
|
|
135
|
+
takes: "files",
|
|
136
|
+
needsFiles: true,
|
|
137
|
+
connection: () => ({
|
|
138
|
+
id: `share:${id}`,
|
|
139
|
+
target: { connectors: [connector.id] },
|
|
140
|
+
actions: UPLOAD_ACTION[connector.id] ? [UPLOAD_ACTION[connector.id]] : ["files_write"],
|
|
141
|
+
title: connector.name,
|
|
142
|
+
}),
|
|
143
|
+
fields: [
|
|
144
|
+
{
|
|
145
|
+
id: "folder",
|
|
146
|
+
kind: "text",
|
|
147
|
+
label: line("shareFolder"),
|
|
148
|
+
placeholder: line("shareFolderPlaceholder"),
|
|
149
|
+
help: line("shareFolderHelp"),
|
|
150
|
+
},
|
|
151
|
+
],
|
|
152
|
+
defaults: (scope) => ({ folder: scope.run.definition.title.replace(/\//g, "-") }),
|
|
153
|
+
async send({ values, files }, ctx) {
|
|
154
|
+
const account = await ctx.account();
|
|
155
|
+
if (!account) {
|
|
156
|
+
throw new ShareError(line("shareConnectFirst", { name: connector.name }));
|
|
157
|
+
}
|
|
158
|
+
const folder = (values.folder ?? "").trim();
|
|
159
|
+
const link = await upload(account.ctx, folder, files, (file) => ctx.emit(line("shareUploading", { name: file.name })));
|
|
160
|
+
const where = [connector.name, folder].filter(Boolean).join(" › ");
|
|
161
|
+
return { summary: line("shareSavedFiles", { count: files.length, where }), url: link };
|
|
162
|
+
},
|
|
163
|
+
};
|
|
164
|
+
}
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import { NotedError } from "../model-errors.js";
|
|
2
|
+
import { serverText } from "../texts.js";
|
|
3
|
+
export const line = (server, params) => ({
|
|
4
|
+
server,
|
|
5
|
+
params,
|
|
6
|
+
});
|
|
7
|
+
/** What is said, in a language. */
|
|
8
|
+
export function sayIn(lang, said) {
|
|
9
|
+
return typeof said === "string" ? said : serverText(lang, said.server, said.params);
|
|
10
|
+
}
|
|
11
|
+
/** A failure the person can read and act on; anything else is reported as "did not work". */
|
|
12
|
+
export class ShareError extends NotedError {
|
|
13
|
+
}
|
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
import { note } from "@engenty-
|
|
2
|
-
import { firstRowAt, newCells, patchCells, rowKey, STORE_LIMITS, } from "@engenty-
|
|
3
|
-
import { cleanPath, mimeForPath } from "@engenty-
|
|
1
|
+
import { note } from "@engenty/wizards-shared/run";
|
|
2
|
+
import { firstRowAt, newCells, patchCells, rowKey, STORE_LIMITS, } from "@engenty/wizards-shared/store";
|
|
3
|
+
import { cleanPath, mimeForPath } from "@engenty/wizards-shared/workspace";
|
|
4
4
|
import { and, asc, eq, inArray, like, lt, sql } from "drizzle-orm";
|
|
5
5
|
import { nanoid } from "nanoid";
|
|
6
6
|
import { db, schema } from "../db/client.js";
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { firstRowAt, newCells, patchCells, rowKey, STORE_LIMITS, } from "@engenty-
|
|
1
|
+
import { firstRowAt, newCells, patchCells, rowKey, STORE_LIMITS, } from "@engenty/wizards-shared/store";
|
|
2
2
|
import { and, asc, eq, inArray } from "drizzle-orm";
|
|
3
3
|
import { nanoid } from "nanoid";
|
|
4
4
|
import { db, schema } from "../db/client.js";
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { textOf } from "@engenty-
|
|
1
|
+
import { textOf } from "@engenty/wizards-shared/texts";
|
|
2
2
|
/**
|
|
3
3
|
* What the server says itself, in each language: answers to a request, the lines about the
|
|
4
4
|
* assistants' work, the words of what it makes for a person (a first space, a new wizard).
|
|
@@ -101,6 +101,60 @@ const EN = {
|
|
|
101
101
|
ownKeysNotInPlan: "Your own API keys aren’t part of the {plan} plan.",
|
|
102
102
|
ownKeysNotInTeamPlan: "Your own API keys aren’t part of the team’s plan.",
|
|
103
103
|
planUnavailable: "The team’s plan can’t be checked right now. Try again later.",
|
|
104
|
+
shareEmailName: "Email",
|
|
105
|
+
shareEmailDescription: "As attachments to one or more addresses",
|
|
106
|
+
shareTo: "To",
|
|
107
|
+
shareToPlaceholder: "name@company.com, second@address.com",
|
|
108
|
+
shareSubject: "Subject",
|
|
109
|
+
shareMessage: "Message",
|
|
110
|
+
shareNotAnAddress: "“{address}” is not an email address.",
|
|
111
|
+
shareNoAddress: "Give at least one address.",
|
|
112
|
+
shareTooManyAddresses: "At most {max} addresses per email.",
|
|
113
|
+
shareAttachmentsTooLarge: "The attachments are {size} – too large for an email. Choose fewer files or share the link.",
|
|
114
|
+
shareMailboxLimit: "The attachments are too large for this mailbox (at most 3 MB). Choose fewer files or share the link.",
|
|
115
|
+
shareVisitorMails: "From here, one run sends {mails} emails to at most {recipients} addresses each.",
|
|
116
|
+
shareConnectMailbox: "Connect a mailbox first.",
|
|
117
|
+
shareSendingFrom: "Sending from {account}",
|
|
118
|
+
shareSentTo: "Sent to {to}",
|
|
119
|
+
shareSlackDescription: "As a message in a channel",
|
|
120
|
+
shareChannel: "Channel",
|
|
121
|
+
shareConnectFirst: "Connect {name} first.",
|
|
122
|
+
sharePostingAs: "Posting as {account}",
|
|
123
|
+
sharePostedSlack: "Posted to Slack",
|
|
124
|
+
shareStorageDescription: "Save the files into a folder",
|
|
125
|
+
shareFolder: "Folder",
|
|
126
|
+
shareFolderPlaceholder: "e.g. Reports/2026",
|
|
127
|
+
shareFolderHelp: "Created if it doesn’t exist yet. Empty = the top level.",
|
|
128
|
+
shareUploading: "Uploading {name}",
|
|
129
|
+
shareSavedFiles: {
|
|
130
|
+
one: "{count} file saved to {where}",
|
|
131
|
+
other: "{count} files saved to {where}",
|
|
132
|
+
},
|
|
133
|
+
shareConnectorDescription: "The wizard does it in your account and asks before every change",
|
|
134
|
+
shareWhatHappens: "What should happen with it?",
|
|
135
|
+
shareWhatHappensPlaceholder: "e.g. Create a new page under Marketing and put the text in.",
|
|
136
|
+
shareWebsiteDescription: "The wizard posts it in the browser – you sign in yourself",
|
|
137
|
+
shareWebsiteAddress: "Address of the website",
|
|
138
|
+
shareWhatThere: "What should happen there?",
|
|
139
|
+
shareWhatTherePlaceholder: "e.g. Create a new blog post as a draft: title, text and the image as cover.",
|
|
140
|
+
sharePostInstructions: "Create a new post with the result’s text and attach the files.",
|
|
141
|
+
shareAdInstructions: "Create a new ad as a draft: the result’s text as the ad copy, the image as its creative. Don’t publish it.",
|
|
142
|
+
shareNoCredits: "This wizard has no credits left right now. Try again later.",
|
|
143
|
+
shareNoReport: "The wizard didn’t say whether it worked.",
|
|
144
|
+
shareCheckInput: "Check what you entered.",
|
|
145
|
+
shareRequired: "Fill this in.",
|
|
146
|
+
shareNeedsHttps: "Give an https:// address.",
|
|
147
|
+
sharePickFile: "Choose at least one file.",
|
|
148
|
+
shareConnectAccount: "Connect the account first.",
|
|
149
|
+
sharePreparing: "Preparing the files",
|
|
150
|
+
shareFailed: "That didn’t work",
|
|
151
|
+
shareTargetUnknown: "This result has no such target.",
|
|
152
|
+
shareOnlyDone: "Only finished results can be shared.",
|
|
153
|
+
shareBusy: "Something is being sent right now.",
|
|
154
|
+
shareTooOften: "This run has sent very often already.",
|
|
155
|
+
shareInterrupted: "Sending was interrupted. Try again.",
|
|
156
|
+
shareNotInResult: "“{name}” ({format}) is not part of this result.",
|
|
157
|
+
shareCannotRender: "“{name}” couldn’t be made as {format}. Choose another format.",
|
|
104
158
|
};
|
|
105
159
|
const DE = {
|
|
106
160
|
keyUnknown: "Der Anbieter kennt diesen API-Key nicht.",
|
|
@@ -199,6 +253,60 @@ const DE = {
|
|
|
199
253
|
ownKeysNotInPlan: "Eigene API-Keys gibt es nicht im Paket {plan}.",
|
|
200
254
|
ownKeysNotInTeamPlan: "Eigene API-Keys gibt es nicht im Paket des Teams.",
|
|
201
255
|
planUnavailable: "Das Paket des Teams lässt sich gerade nicht prüfen. Versuch es später noch einmal.",
|
|
256
|
+
shareEmailName: "E-Mail",
|
|
257
|
+
shareEmailDescription: "Als Anhang an eine oder mehrere Adressen",
|
|
258
|
+
shareTo: "An",
|
|
259
|
+
shareToPlaceholder: "name@firma.at, zweite@adresse.at",
|
|
260
|
+
shareSubject: "Betreff",
|
|
261
|
+
shareMessage: "Nachricht",
|
|
262
|
+
shareNotAnAddress: "„{address}“ ist keine E-Mail-Adresse.",
|
|
263
|
+
shareNoAddress: "Gib mindestens eine Adresse an.",
|
|
264
|
+
shareTooManyAddresses: "Höchstens {max} Adressen pro E-Mail.",
|
|
265
|
+
shareAttachmentsTooLarge: "Die Anhänge sind mit {size} zu groß für eine E-Mail. Wähle weniger Dateien oder teile den Link.",
|
|
266
|
+
shareMailboxLimit: "Die Anhänge sind zu groß für dieses Postfach (höchstens 3 MB). Wähle weniger Dateien oder teile den Link.",
|
|
267
|
+
shareVisitorMails: "Von hier gehen pro Lauf {mails} E-Mails an je höchstens {recipients} Adressen.",
|
|
268
|
+
shareConnectMailbox: "Verbinde zuerst ein Postfach.",
|
|
269
|
+
shareSendingFrom: "Sendet von {account}",
|
|
270
|
+
shareSentTo: "Gesendet an {to}",
|
|
271
|
+
shareSlackDescription: "Als Nachricht in einen Kanal",
|
|
272
|
+
shareChannel: "Kanal",
|
|
273
|
+
shareConnectFirst: "Verbinde zuerst {name}.",
|
|
274
|
+
sharePostingAs: "Postet als {account}",
|
|
275
|
+
sharePostedSlack: "In Slack gepostet",
|
|
276
|
+
shareStorageDescription: "Dateien in einen Ordner legen",
|
|
277
|
+
shareFolder: "Ordner",
|
|
278
|
+
shareFolderPlaceholder: "z. B. Berichte/2026",
|
|
279
|
+
shareFolderHelp: "Wird angelegt, wenn es ihn noch nicht gibt. Leer = oberste Ebene.",
|
|
280
|
+
shareUploading: "Lädt {name} hoch",
|
|
281
|
+
shareSavedFiles: {
|
|
282
|
+
one: "{count} Datei in {where} gespeichert",
|
|
283
|
+
other: "{count} Dateien in {where} gespeichert",
|
|
284
|
+
},
|
|
285
|
+
shareConnectorDescription: "Der Wizard erledigt es in deinem Konto und fragt vor jeder Änderung",
|
|
286
|
+
shareWhatHappens: "Was soll damit passieren?",
|
|
287
|
+
shareWhatHappensPlaceholder: "z. B. Lege eine neue Seite im Bereich Marketing an und füge den Text ein.",
|
|
288
|
+
shareWebsiteDescription: "Der Wizard postet es im Browser – du meldest dich selbst an",
|
|
289
|
+
shareWebsiteAddress: "Adresse der Website",
|
|
290
|
+
shareWhatThere: "Was soll dort passieren?",
|
|
291
|
+
shareWhatTherePlaceholder: "z. B. Lege einen neuen Blog-Beitrag als Entwurf an: Titel, Text und das Bild als Titelbild.",
|
|
292
|
+
sharePostInstructions: "Erstelle einen neuen Beitrag mit dem Text des Ergebnisses und hänge die Dateien an.",
|
|
293
|
+
shareAdInstructions: "Lege eine neue Anzeige als Entwurf an: der Text des Ergebnisses als Anzeigentext, das Bild als Motiv. Nicht veröffentlichen.",
|
|
294
|
+
shareNoCredits: "Dieser Wizard hat gerade kein Guthaben mehr. Versuch es später noch einmal.",
|
|
295
|
+
shareNoReport: "Der Wizard hat nicht gemeldet, ob es geklappt hat.",
|
|
296
|
+
shareCheckInput: "Prüfe deine Angaben.",
|
|
297
|
+
shareRequired: "Fülle das aus.",
|
|
298
|
+
shareNeedsHttps: "Gib eine Adresse mit https:// an.",
|
|
299
|
+
sharePickFile: "Wähle mindestens eine Datei.",
|
|
300
|
+
shareConnectAccount: "Verbinde zuerst das Konto.",
|
|
301
|
+
sharePreparing: "Bereitet die Dateien vor",
|
|
302
|
+
shareFailed: "Das hat nicht geklappt",
|
|
303
|
+
shareTargetUnknown: "Dieses Ziel gibt es für dieses Ergebnis nicht.",
|
|
304
|
+
shareOnlyDone: "Nur fertige Ergebnisse lassen sich teilen.",
|
|
305
|
+
shareBusy: "Es wird gerade schon etwas gesendet.",
|
|
306
|
+
shareTooOften: "Dieser Lauf hat schon sehr oft gesendet.",
|
|
307
|
+
shareInterrupted: "Das Senden wurde unterbrochen. Versuch es noch einmal.",
|
|
308
|
+
shareNotInResult: "„{name}“ ({format}) gehört nicht zu diesem Ergebnis.",
|
|
309
|
+
shareCannotRender: "„{name}“ ließ sich nicht als {format} erstellen. Wähle ein anderes Format.",
|
|
202
310
|
};
|
|
203
311
|
export const SERVER_TEXTS = { en: EN, de: DE };
|
|
204
312
|
/** A line of the server's table in a language; English where the language lacks it. */
|