@sulala/agent 0.1.6 → 0.1.8
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 +42 -27
- package/context/airtable.md +35 -0
- package/context/asana.md +37 -0
- package/context/bluesky.md +26 -91
- package/context/calendar.md +63 -0
- package/context/country-info.md +13 -0
- package/context/create-skill.md +128 -0
- package/context/discord.md +30 -0
- package/context/docs.md +29 -0
- package/context/drive.md +49 -0
- package/context/dropbox.md +39 -0
- package/context/facebook.md +47 -0
- package/context/fetch-form-api.md +16 -0
- package/context/figma.md +30 -0
- package/context/github.md +58 -0
- package/context/gmail.md +52 -0
- package/context/google.md +28 -0
- package/context/hellohub.md +29 -0
- package/context/jira.md +46 -0
- package/context/linear.md +40 -0
- package/context/notion.md +45 -0
- package/context/portal-integrations.md +42 -0
- package/context/post-to-x.md +50 -0
- package/context/sheets.md +47 -0
- package/context/slack.md +48 -0
- package/context/slides.md +35 -0
- package/context/stripe.md +38 -0
- package/context/tes.md +7 -0
- package/context/test.md +7 -0
- package/context/zoom.md +28 -0
- package/dist/agent/google/calendar.d.ts +2 -0
- package/dist/agent/google/calendar.d.ts.map +1 -0
- package/dist/agent/google/calendar.js +119 -0
- package/dist/agent/google/calendar.js.map +1 -0
- package/dist/agent/google/drive.d.ts +2 -0
- package/dist/agent/google/drive.d.ts.map +1 -0
- package/dist/agent/google/drive.js +51 -0
- package/dist/agent/google/drive.js.map +1 -0
- package/dist/agent/google/get-token.d.ts +7 -0
- package/dist/agent/google/get-token.d.ts.map +1 -0
- package/dist/agent/google/get-token.js +37 -0
- package/dist/agent/google/get-token.js.map +1 -0
- package/dist/agent/google/gmail.d.ts +2 -0
- package/dist/agent/google/gmail.d.ts.map +1 -0
- package/dist/agent/google/gmail.js +138 -0
- package/dist/agent/google/gmail.js.map +1 -0
- package/dist/agent/google/index.d.ts +2 -0
- package/dist/agent/google/index.d.ts.map +1 -0
- package/dist/agent/google/index.js +13 -0
- package/dist/agent/google/index.js.map +1 -0
- package/dist/agent/loop.d.ts +8 -0
- package/dist/agent/loop.d.ts.map +1 -1
- package/dist/agent/loop.js +226 -40
- package/dist/agent/loop.js.map +1 -1
- package/dist/agent/memory.d.ts +21 -0
- package/dist/agent/memory.d.ts.map +1 -0
- package/dist/agent/memory.js +33 -0
- package/dist/agent/memory.js.map +1 -0
- package/dist/agent/pending-actions.d.ts +21 -0
- package/dist/agent/pending-actions.d.ts.map +1 -0
- package/dist/agent/pending-actions.js +65 -0
- package/dist/agent/pending-actions.js.map +1 -0
- package/dist/agent/pi-runner.d.ts +27 -0
- package/dist/agent/pi-runner.d.ts.map +1 -0
- package/dist/agent/pi-runner.js +300 -0
- package/dist/agent/pi-runner.js.map +1 -0
- package/dist/agent/skill-generate.d.ts +63 -0
- package/dist/agent/skill-generate.d.ts.map +1 -0
- package/dist/agent/skill-generate.js +128 -0
- package/dist/agent/skill-generate.js.map +1 -0
- package/dist/agent/skill-install.d.ts.map +1 -1
- package/dist/agent/skill-install.js +80 -31
- package/dist/agent/skill-install.js.map +1 -1
- package/dist/agent/skill-templates.d.ts +17 -0
- package/dist/agent/skill-templates.d.ts.map +1 -0
- package/dist/agent/skill-templates.js +26 -0
- package/dist/agent/skill-templates.js.map +1 -0
- package/dist/agent/skills-config.d.ts +24 -2
- package/dist/agent/skills-config.d.ts.map +1 -1
- package/dist/agent/skills-config.js +108 -9
- package/dist/agent/skills-config.js.map +1 -1
- package/dist/agent/skills-watcher.js +1 -1
- package/dist/agent/skills.d.ts +9 -3
- package/dist/agent/skills.d.ts.map +1 -1
- package/dist/agent/skills.js +104 -9
- package/dist/agent/skills.js.map +1 -1
- package/dist/agent/tools.d.ts +25 -3
- package/dist/agent/tools.d.ts.map +1 -1
- package/dist/agent/tools.integrations.test.d.ts +2 -0
- package/dist/agent/tools.integrations.test.d.ts.map +1 -0
- package/dist/agent/tools.integrations.test.js +269 -0
- package/dist/agent/tools.integrations.test.js.map +1 -0
- package/dist/agent/tools.js +692 -39
- package/dist/agent/tools.js.map +1 -1
- package/dist/ai/orchestrator.d.ts +6 -1
- package/dist/ai/orchestrator.d.ts.map +1 -1
- package/dist/ai/orchestrator.js +499 -212
- package/dist/ai/orchestrator.js.map +1 -1
- package/dist/ai/pricing.d.ts +6 -0
- package/dist/ai/pricing.d.ts.map +1 -0
- package/dist/ai/pricing.js +39 -0
- package/dist/ai/pricing.js.map +1 -0
- package/dist/channels/discord.d.ts +15 -0
- package/dist/channels/discord.d.ts.map +1 -0
- package/dist/channels/discord.js +55 -0
- package/dist/channels/discord.js.map +1 -0
- package/dist/channels/stripe.d.ts +15 -0
- package/dist/channels/stripe.d.ts.map +1 -0
- package/dist/channels/stripe.js +58 -0
- package/dist/channels/stripe.js.map +1 -0
- package/dist/channels/telegram.d.ts +60 -0
- package/dist/channels/telegram.d.ts.map +1 -0
- package/dist/channels/telegram.js +562 -0
- package/dist/channels/telegram.js.map +1 -0
- package/dist/cli.js +69 -11
- package/dist/cli.js.map +1 -1
- package/dist/config.d.ts +14 -0
- package/dist/config.d.ts.map +1 -1
- package/dist/config.js +91 -2
- package/dist/config.js.map +1 -1
- package/dist/db/index.d.ts +83 -0
- package/dist/db/index.d.ts.map +1 -1
- package/dist/db/index.js +174 -2
- package/dist/db/index.js.map +1 -1
- package/dist/db/schema.sql +35 -0
- package/dist/gateway/server.d.ts.map +1 -1
- package/dist/gateway/server.js +1224 -29
- package/dist/gateway/server.js.map +1 -1
- package/dist/index.js +149 -6
- package/dist/index.js.map +1 -1
- package/dist/ollama-setup.d.ts +27 -0
- package/dist/ollama-setup.d.ts.map +1 -0
- package/dist/ollama-setup.js +191 -0
- package/dist/ollama-setup.js.map +1 -0
- package/dist/onboard-env.d.ts +1 -1
- package/dist/onboard-env.d.ts.map +1 -1
- package/dist/onboard-env.js +3 -0
- package/dist/onboard-env.js.map +1 -1
- package/dist/onboard.d.ts +3 -1
- package/dist/onboard.d.ts.map +1 -1
- package/dist/onboard.js +9 -4
- package/dist/onboard.js.map +1 -1
- package/dist/plugins/index.d.ts +10 -0
- package/dist/plugins/index.d.ts.map +1 -1
- package/dist/plugins/index.js +32 -0
- package/dist/plugins/index.js.map +1 -1
- package/dist/redact.d.ts +15 -0
- package/dist/redact.d.ts.map +1 -0
- package/dist/redact.js +56 -0
- package/dist/redact.js.map +1 -0
- package/dist/scheduler/cron.d.ts +21 -0
- package/dist/scheduler/cron.d.ts.map +1 -1
- package/dist/scheduler/cron.js +60 -0
- package/dist/scheduler/cron.js.map +1 -1
- package/dist/system-capabilities.d.ts +11 -0
- package/dist/system-capabilities.d.ts.map +1 -0
- package/dist/system-capabilities.js +109 -0
- package/dist/system-capabilities.js.map +1 -0
- package/dist/types.d.ts +62 -3
- package/dist/types.d.ts.map +1 -1
- package/dist/watcher/index.d.ts +2 -0
- package/dist/watcher/index.d.ts.map +1 -1
- package/dist/watcher/index.js +31 -1
- package/dist/watcher/index.js.map +1 -1
- package/dist/workspace-automations.d.ts +16 -0
- package/dist/workspace-automations.d.ts.map +1 -0
- package/dist/workspace-automations.js +133 -0
- package/dist/workspace-automations.js.map +1 -0
- package/package.json +19 -3
- package/registry/bluesky.md +12 -89
- package/registry/skills-registry.json +6 -0
- package/src/db/schema.sql +35 -0
- package/src/index.ts +159 -6
package/README.md
CHANGED
|
@@ -2,18 +2,27 @@
|
|
|
2
2
|
|
|
3
3
|
A local-first platform that combines **file monitoring**, **task scheduling**, **AI orchestration**, and a **plugin system**. Runs on `127.0.0.1` and stays on your machine.
|
|
4
4
|
|
|
5
|
+
## Contents
|
|
6
|
+
|
|
7
|
+
- [Architecture](#architecture-high-level)
|
|
8
|
+
- [Quick start](#quick-start)
|
|
9
|
+
- [Hub & integrations](#hub--integrations)
|
|
10
|
+
- [Requirements](#requirements)
|
|
11
|
+
- [Project layout](#project-layout)
|
|
12
|
+
- [Security](#security)
|
|
13
|
+
|
|
5
14
|
## Architecture (high level)
|
|
6
15
|
|
|
7
16
|
| Layer | Role |
|
|
8
17
|
|-------|------|
|
|
9
|
-
| **Gateway** | REST + WebSocket API on `localhost:
|
|
18
|
+
| **Gateway** | REST + WebSocket API on `localhost:2026`; auth and request handling |
|
|
10
19
|
| **File watcher** | Real-time folder watch (add/change/delete) → event triggers |
|
|
11
20
|
| **Task scheduler** | Cron-like scheduling + queue with retries and failure handling |
|
|
12
21
|
| **AI orchestration** | Single interface to multiple providers (OpenAI, OpenRouter, Claude, Gemini, Ollama); routing and rate limits |
|
|
13
22
|
| **Plugins** | Scripts and integrations that hook into events, tasks, and AI |
|
|
14
23
|
| **Persistence** | SQLite for tasks, file state, logs, and AI results |
|
|
15
24
|
|
|
16
|
-
See [docs/
|
|
25
|
+
See [docs/architecture.md](docs/architecture.md) and the project diagram for full layout.
|
|
17
26
|
|
|
18
27
|
## Quick start
|
|
19
28
|
|
|
@@ -25,7 +34,7 @@ sulala onboard
|
|
|
25
34
|
sulala onboard --install-daemon
|
|
26
35
|
```
|
|
27
36
|
|
|
28
|
-
Then open http://127.0.0.1:
|
|
37
|
+
Then open http://127.0.0.1:2026 (dashboard and API). **Default LLM is Ollama** (local, no API key). On first run, if Ollama is not installed, the app will start the official installer for your OS (Mac/Linux/Windows). You can optionally add API keys at http://127.0.0.1:2026/onboard to use OpenAI, Claude, Gemini, or OpenRouter instead.
|
|
29
38
|
|
|
30
39
|
**Or run from source (clone this repo):**
|
|
31
40
|
|
|
@@ -40,21 +49,12 @@ cp .env.example .env
|
|
|
40
49
|
npm start
|
|
41
50
|
```
|
|
42
51
|
|
|
43
|
-
**Development:** `npm run dev` runs
|
|
52
|
+
**Development:** `npm run dev` runs with `tsx watch` (restarts on file changes). Production: `npm run build` then `node dist/index.js`. Tests: `npm test`; lint: `npm run lint`.
|
|
44
53
|
|
|
45
54
|
**Dashboard (React + Vite + shadcn/ui):**
|
|
46
55
|
|
|
47
|
-
- **Dev:** run
|
|
48
|
-
|
|
49
|
-
npm run dashboard
|
|
50
|
-
```
|
|
51
|
-
Then open the URL Vite prints (e.g. http://localhost:5173). Set `VITE_GATEWAY_URL=http://127.0.0.1:3000` in `dashboard/.env` if the API is on another host.
|
|
52
|
-
- **Production:** build and serve from the gateway (one origin):
|
|
53
|
-
```bash
|
|
54
|
-
npm run dashboard:build
|
|
55
|
-
npm start
|
|
56
|
-
```
|
|
57
|
-
Then open http://127.0.0.1:3000 — the gateway serves the built dashboard and the API.
|
|
56
|
+
- **Dev:** `npm run dashboard` — UI on its own port (e.g. 5173); gateway on 2026. Set `VITE_GATEWAY_URL=http://127.0.0.1:2026` in `dashboard/.env` if needed.
|
|
57
|
+
- **Production:** `npm run dashboard:build` then `npm start` — gateway serves the built dashboard and API at http://127.0.0.1:2026.
|
|
58
58
|
|
|
59
59
|
**CLI** (from project root):
|
|
60
60
|
|
|
@@ -74,29 +74,44 @@ sulala skill update
|
|
|
74
74
|
sulala skill uninstall apple-notes [--global]
|
|
75
75
|
```
|
|
76
76
|
|
|
77
|
-
**Skill commands:**
|
|
77
|
+
**Skill commands:**
|
|
78
|
+
|
|
79
|
+
- `sulala skill list` — list registry skills
|
|
80
|
+
- `sulala skill install <slug> [--global]` — install to workspace (default) or `~/.sulala/skills`
|
|
81
|
+
- `sulala skill update` — refresh installed skills from the registry
|
|
82
|
+
- `sulala skill uninstall <slug> [--global]` — remove a skill
|
|
83
|
+
- `sulala init [dir]` — create config/context/registry and copy `.env.example` → `.env`
|
|
84
|
+
|
|
85
|
+
**Onboard & daemon (global install):** Run `sulala onboard` to create `~/.sulala` and a default `.env`; the browser opens to **http://127.0.0.1:2026/onboard** to add API keys (saved to `~/.sulala/.env`). Run `sulala onboard --install-daemon` to install a background service (launchd on macOS, systemd on Linux) so the agent runs at login. Logs: `~/.sulala/logs/`. Use `sulala stop` / `sulala start` to stop or start the daemon; `sulala onboard --uninstall-daemon` to remove it.
|
|
78
86
|
|
|
79
|
-
**
|
|
87
|
+
**Works on any device:** The published package includes the default skills registry and bundled skills (`registry/`, `context/`), so `sulala skill list` and the agent work out of the box. Optionally set `SKILLS_REGISTRY_URL` for a remote skills store.
|
|
80
88
|
|
|
81
|
-
**
|
|
89
|
+
**Hub & integrations**
|
|
82
90
|
|
|
83
|
-
|
|
91
|
+
- **Skills hub:** [hub.sulala.ai](https://hub.sulala.ai) — upload skills for the agent and share them with others; install via `sulala skill list` / `sulala skill install` when using that registry.
|
|
92
|
+
- **Integrations:** Add more integrations (Gmail, Slack, GitHub, Calendar, etc.) for the agent; use the dashboard Integrations area or the integrations catalog to connect and manage them.
|
|
93
|
+
- **Portal (testing):** [portal.sulala.ai](https://portal.sulala.ai) — connect OAuth apps for testing (Gmail, Calendar, Slack, GitHub, etc.). Set `PORTAL_GATEWAY_URL` and `PORTAL_API_KEY` in the agent (e.g. in dashboard Settings → Portal) so it can list connections and get tokens. See [docs/sulalahub.md](docs/sulalahub.md) and `context/portal-integrations.md`.
|
|
84
94
|
|
|
85
|
-
**
|
|
86
|
-
|
|
87
|
-
-
|
|
88
|
-
|
|
95
|
+
**Env for CLI/gateway:** Set `GATEWAY_URL` and optionally `GATEWAY_API_KEY` for gateway commands; `SULALA_SKILLS_DIR` and `AGENT_CONTEXT_PATH` for skill paths.
|
|
96
|
+
|
|
97
|
+
**Agent runner:** Multi-turn sessions with optional tool use. See [docs/roadmap-agent-runner.md](docs/roadmap-agent-runner.md) and [docs/agent-api.md](docs/agent-api.md).
|
|
98
|
+
|
|
99
|
+
- `GET /api/agent/sessions` — list sessions (query: `limit`)
|
|
100
|
+
- `POST /api/agent/sessions` — create or get session (body: `{ "session_key": "my-chat", "meta": {} }`)
|
|
101
|
+
- `GET /api/agent/sessions/:id` — get session and message history
|
|
89
102
|
- `POST /api/agent/sessions/:id/messages` — send a message and run the agent turn (body: `{ "message": "…", "system_prompt": "…", "provider": "openai", "timeout_ms": 300000 }`). Returns `{ finalContent, messages, turnCount }`. Runs are serialized per session; client disconnect or timeout aborts the run (`AGENT_TIMEOUT_MS` in env or `timeout_ms` in body).
|
|
90
103
|
|
|
91
|
-
**AI models:**
|
|
104
|
+
**AI models:** OpenAI, OpenRouter, Claude, Gemini, Ollama (local). See [docs/models.md](docs/models.md) for model IDs and env vars.
|
|
92
105
|
|
|
93
|
-
**Config:** Watched folders
|
|
106
|
+
**Config:** Watched folders: `.env` (`WATCH_FOLDERS`) or `config/watched.json` (array `folders`); both are merged. Gateway auth: `GATEWAY_API_KEY`; webhooks: `WEBHOOK_URL` or `WEBHOOK_URLS` (optional `WEBHOOK_SECRET`). Default AI is **Ollama** (no key; installs automatically if missing). Override with `AI_DEFAULT_PROVIDER=openai` (or `claude`, `gemini`, `openrouter`) and set the corresponding API keys. Optional: `OLLAMA_BASE_URL`, `RATE_LIMIT_MAX`, `RATE_LIMIT_WINDOW_MS`. See `.env.example` and [docs/config.md](docs/config.md).
|
|
107
|
+
|
|
108
|
+
**Docker:** Dashboard is served from the gateway.
|
|
94
109
|
|
|
95
|
-
**Docker:** Build and run with dashboard served from the gateway:
|
|
96
110
|
```bash
|
|
97
111
|
docker compose up --build
|
|
98
112
|
```
|
|
99
|
-
|
|
113
|
+
|
|
114
|
+
Open http://localhost:2026. Mount `./config` for watched folders; data is in a named volume.
|
|
100
115
|
|
|
101
116
|
## Requirements
|
|
102
117
|
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: airtable
|
|
3
|
+
description: Use Airtable (bases, records) via the Portal. When the user asks about Airtable bases or records, list connections with list_integrations_connections (provider airtable) and use run_command with curl to the Airtable API or gateway.
|
|
4
|
+
metadata:
|
|
5
|
+
{
|
|
6
|
+
"sulala": {
|
|
7
|
+
"emoji": "📊",
|
|
8
|
+
"requires": { "bins": ["curl"] }
|
|
9
|
+
}
|
|
10
|
+
}
|
|
11
|
+
---
|
|
12
|
+
|
|
13
|
+
# Airtable
|
|
14
|
+
|
|
15
|
+
1. **list_integrations_connections** with `provider: "airtable"` → get `connection_id`.
|
|
16
|
+
2. **get_connection_token** with that `connection_id` → returns `accessToken` (do not curl the portal).
|
|
17
|
+
3. **run_command (curl)** with `Authorization: Bearer <accessToken>` and `Content-Type: application/json`.
|
|
18
|
+
|
|
19
|
+
Airtable API uses **base ID** and **table name or ID**. Add `api.airtable.com` to **ALLOWED_CURL_HOSTS**. Official docs: https://airtable.com/developers/web/api/introduction
|
|
20
|
+
|
|
21
|
+
---
|
|
22
|
+
|
|
23
|
+
## Bases and tables
|
|
24
|
+
|
|
25
|
+
- **List bases**: `GET https://api.airtable.com/v0/meta/bases`. Returns `bases[].id`, `bases[].name`. Use base `id` in table URLs.
|
|
26
|
+
- **List tables** (schema) in a base: `GET https://api.airtable.com/v0/meta/bases/<baseId>/tables`. Returns table names and field definitions.
|
|
27
|
+
|
|
28
|
+
---
|
|
29
|
+
|
|
30
|
+
## Records
|
|
31
|
+
|
|
32
|
+
- **List records** (one table): `GET https://api.airtable.com/v0/<baseId>/<tableNameOrId>?maxRecords=20`. Optional: `?filterByFormula=<formula>`, `?sort[0][field]=Name`. Returns `records[].id`, `records[].fields`.
|
|
33
|
+
- **Create record**: `POST https://api.airtable.com/v0/<baseId>/<tableNameOrId>` with body `{"fields": {"Name": "Value", "OtherField": "Value"}}`. Field names must match the table schema.
|
|
34
|
+
|
|
35
|
+
Requirements: **PORTAL_GATEWAY_URL**, **PORTAL_API_KEY**; user must connect Airtable in the Portal. Base must be shared with the connected account.
|
package/context/asana.md
ADDED
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: asana
|
|
3
|
+
description: Use Asana (workspaces, projects, tasks) via the Portal. When the user asks about Asana tasks or projects, list connections with list_integrations_connections (provider asana) and use run_command with curl to the Asana API or gateway.
|
|
4
|
+
metadata:
|
|
5
|
+
{
|
|
6
|
+
"sulala": {
|
|
7
|
+
"emoji": "✅",
|
|
8
|
+
"requires": { "bins": ["curl"] }
|
|
9
|
+
}
|
|
10
|
+
}
|
|
11
|
+
---
|
|
12
|
+
|
|
13
|
+
# Asana
|
|
14
|
+
|
|
15
|
+
1. **list_integrations_connections** with `provider: "asana"` → get `connection_id`.
|
|
16
|
+
2. **get_connection_token** with that `connection_id` → returns `accessToken` (do not curl the portal).
|
|
17
|
+
3. **run_command (curl)** with `Authorization: Bearer <accessToken>` and `Content-Type: application/json`.
|
|
18
|
+
|
|
19
|
+
Base URL: `https://app.asana.com/api/1.0`. Add `app.asana.com` to **ALLOWED_CURL_HOSTS**. Official docs: https://developers.asana.com/reference/rest-api-reference
|
|
20
|
+
|
|
21
|
+
---
|
|
22
|
+
|
|
23
|
+
## Workspaces and projects
|
|
24
|
+
|
|
25
|
+
- **List workspaces**: `GET https://app.asana.com/api/1.0/workspaces`. Returns `data[].gid`, `data[].name`.
|
|
26
|
+
- **List projects** (in workspace): `GET https://app.asana.com/api/1.0/workspaces/<workspace_gid>/projects`. Returns `data[].gid`, `data[].name`.
|
|
27
|
+
|
|
28
|
+
---
|
|
29
|
+
|
|
30
|
+
## Tasks
|
|
31
|
+
|
|
32
|
+
- **List tasks** (in project): `GET https://app.asana.com/api/1.0/projects/<project_gid>/tasks?opt_fields=name,completed,due_on,notes`. Returns `data[].gid`, `data[].name`, etc. Pagination: `offset`, `limit`.
|
|
33
|
+
- **Create task**: `POST https://app.asana.com/api/1.0/tasks` with header `Content-Type: application/json` and body `{"data": {"name": "Task title", "projects": ["<project_gid>"]}}`. The `projects` value must be an array of one or more project GID **strings** from `GET /workspaces/<workspace_gid>/projects` (e.g. `["1213334948480995"]`). Do **not** send `workspace` when sending `projects`—the API will error. Optional in `data`: `"notes": "Description"`, `"due_on": "YYYY-MM-DD"`. Flow: (1) GET `/workspaces` → pick a workspace `gid`, (2) GET `/workspaces/<workspace_gid>/projects` → pick a project `gid`, (3) POST `/tasks` with `{"data": {"name": "fix bug today", "projects": ["<project_gid>"]}}`.
|
|
34
|
+
- **Get task**: `GET https://app.asana.com/api/1.0/tasks/<task_gid>?opt_fields=name,completed,notes,due_on,projects`.
|
|
35
|
+
- **Update task** (mark complete, etc.): `PUT https://app.asana.com/api/1.0/tasks/<task_gid>` with body `{"data": {"completed": true}}` or `{"data": {"name": "New name"}}`.
|
|
36
|
+
|
|
37
|
+
Requirements: **PORTAL_GATEWAY_URL**, **PORTAL_API_KEY**; user must connect Asana in the Portal.
|
package/context/bluesky.md
CHANGED
|
@@ -1,111 +1,46 @@
|
|
|
1
1
|
---
|
|
2
2
|
name: bluesky
|
|
3
|
-
description: Post to Bluesky (AT Protocol). Use when the user asks to post
|
|
4
|
-
homepage: https://bsky.app
|
|
3
|
+
description: Post to Bluesky (AT Protocol). Use when the user asks to post to Bluesky or share content on Bluesky. Use either (A) Portal OAuth connection and the Bluesky proxy, or (B) app password from skill config (BSKY_HANDLE, BSKY_APP_PASSWORD).
|
|
5
4
|
metadata:
|
|
6
5
|
{
|
|
7
6
|
"sulala": {
|
|
8
7
|
"emoji": "🦋",
|
|
9
|
-
"requires": { "bins": ["curl"
|
|
10
|
-
"primaryEnv": "BSKY_APP_PASSWORD"
|
|
8
|
+
"requires": { "bins": ["curl"] }
|
|
11
9
|
}
|
|
12
10
|
}
|
|
13
11
|
---
|
|
14
12
|
|
|
15
|
-
# Bluesky
|
|
13
|
+
# Bluesky
|
|
16
14
|
|
|
17
|
-
|
|
15
|
+
Two ways to post:
|
|
18
16
|
|
|
19
|
-
|
|
17
|
+
## (A) Portal OAuth (recommended)
|
|
20
18
|
|
|
21
|
-
|
|
19
|
+
1. **list_integrations_connections** with `provider: "bluesky"` → get `connection_id`.
|
|
20
|
+
2. **bluesky_post** with that `connection_id` and the post text (max 300 characters).
|
|
22
21
|
|
|
23
|
-
|
|
22
|
+
Use the **bluesky_post** tool for posting. Do not use run_command (curl) for Bluesky—the correct endpoint is the portal gateway bsky-request, and the tool calls it for you.
|
|
24
23
|
|
|
25
|
-
|
|
26
|
-
- "Share [content] on Bluesky"
|
|
27
|
-
- "Post news from [URL] to Bluesky"
|
|
28
|
-
- "Post a headline about [topic]"
|
|
24
|
+
## (B) App password (skill config)
|
|
29
25
|
|
|
30
|
-
|
|
26
|
+
Set **BSKY_HANDLE** and **BSKY_APP_PASSWORD** (from bsky.app → Settings → App Passwords) in Skills → Bluesky config.
|
|
31
27
|
|
|
32
|
-
|
|
28
|
+
1. **Create session** to get access token:
|
|
29
|
+
```bash
|
|
30
|
+
curl -s -X POST "https://bsky.social/xrpc/com.atproto.server.createSession" \
|
|
31
|
+
-H "Content-Type: application/json" \
|
|
32
|
+
-d '{"identifier":"$BSKY_HANDLE","password":"$BSKY_APP_PASSWORD"}'
|
|
33
|
+
```
|
|
34
|
+
Response has `accessJwt` and `did`.
|
|
33
35
|
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
36
|
+
2. **Create post** (use the JWT and did from step 1):
|
|
37
|
+
```bash
|
|
38
|
+
curl -s -X POST "https://bsky.social/xrpc/com.atproto.repo.createRecord" \
|
|
39
|
+
-H "Content-Type: application/json" \
|
|
40
|
+
-H "Authorization: Bearer <accessJwt>" \
|
|
41
|
+
-d '{"repo":"<did>","collection":"app.bsky.feed.post","record":{"$type":"app.bsky.feed.post","text":"<post text>","createdAt":"<ISO8601>"}}'
|
|
42
|
+
```
|
|
38
43
|
|
|
39
|
-
|
|
44
|
+
Add `bsky.social` to **ALLOWED_CURL_HOSTS**. Optional: **BSKY_PDS** (default https://bsky.social) for a custom PDS.
|
|
40
45
|
|
|
41
|
-
|
|
42
|
-
CONFIG_PATH="${SULALA_CONFIG_PATH:-$HOME/.sulala/config.json}"
|
|
43
|
-
# If using workspace config, use: CONFIG_PATH=".sulala/config.json" (when run from project root)
|
|
44
|
-
BSKY_HANDLE=$(cat "$CONFIG_PATH" 2>/dev/null | python3 -c "import sys,json; d=json.load(sys.stdin); e=d.get('skills',{}).get('entries',{}).get('bluesky',{}); print(e.get('handle','') or e.get('apiKey',''))" 2>/dev/null)
|
|
45
|
-
BSKY_APP_PASSWORD=$(cat "$CONFIG_PATH" 2>/dev/null | python3 -c "import sys,json; d=json.load(sys.stdin); e=d.get('skills',{}).get('entries',{}).get('bluesky',{}); print(e.get('apiKey','') or e.get('password',''))" 2>/dev/null)
|
|
46
|
-
```
|
|
47
|
-
|
|
48
|
-
(Config may use `handle` + `apiKey` for Bluesky. Adjust field names if your config differs.)
|
|
49
|
-
|
|
50
|
-
### 2. Create session (login)
|
|
51
|
-
|
|
52
|
-
**Use `run_command` with `binary: "sh"` and `args: ["-c", "..."]`** so `$BSKY_HANDLE` and `$BSKY_APP_PASSWORD` expand from the environment. Do NOT call curl directly.
|
|
53
|
-
|
|
54
|
-
```bash
|
|
55
|
-
sh -c 'SESSION=$(curl -s -X POST "https://bsky.social/xrpc/com.atproto.server.createSession" -H "Content-Type: application/json" -d "{\"identifier\": \"$BSKY_HANDLE\", \"password\": \"$BSKY_APP_PASSWORD\"}"); echo "$SESSION"'
|
|
56
|
-
```
|
|
57
|
-
|
|
58
|
-
Then parse the JSON output with `python3 -c "import sys,json; d=json.load(sys.stdin); print(d.get('accessJwt',''), d.get('did',''))"` to get `ACCESS_TOKEN` and `DID`.
|
|
59
|
-
|
|
60
|
-
If the session response contains `"error"` or `"AuthenticationRequired"`, report: "Bluesky login failed. Check BSKY_HANDLE and BSKY_APP_PASSWORD in .env."
|
|
61
|
-
|
|
62
|
-
### 3. Create post
|
|
63
|
-
|
|
64
|
-
Text must be JSON-escaped. Use python to build the payload. **Use `run_command` with `binary: "sh"`** so `$ACCESS_TOKEN`, `$DID`, and other vars expand.
|
|
65
|
-
|
|
66
|
-
Run a single `sh -c` that chains steps 2 and 3 so SESSION, ACCESS_TOKEN, DID persist in the same shell:
|
|
67
|
-
|
|
68
|
-
```bash
|
|
69
|
-
sh -c '
|
|
70
|
-
SESSION=$(curl -s -X POST "https://bsky.social/xrpc/com.atproto.server.createSession" \
|
|
71
|
-
-H "Content-Type: application/json" \
|
|
72
|
-
-d "{\"identifier\": \"$BSKY_HANDLE\", \"password\": \"$BSKY_APP_PASSWORD\"}");
|
|
73
|
-
ACCESS_TOKEN=$(echo "$SESSION" | python3 -c "import sys,json; d=json.load(sys.stdin); print(d.get(\"accessJwt\",\"\"))");
|
|
74
|
-
DID=$(echo "$SESSION" | python3 -c "import sys,json; d=json.load(sys.stdin); print(d.get(\"did\",\"\"))");
|
|
75
|
-
if [ -z "$ACCESS_TOKEN" ]; then echo "Login failed"; exit 1; fi;
|
|
76
|
-
POST_TEXT="Your post content here";
|
|
77
|
-
POST_JSON=$(python3 -c "import sys,json; print(json.dumps(sys.argv[1]))" "$POST_TEXT");
|
|
78
|
-
NOW=$(date -u +"%Y-%m-%dT%H:%M:%S.000Z");
|
|
79
|
-
curl -s -X POST "https://bsky.social/xrpc/com.atproto.repo.createRecord" \
|
|
80
|
-
-H "Authorization: Bearer $ACCESS_TOKEN" \
|
|
81
|
-
-H "Content-Type: application/json" \
|
|
82
|
-
-d "{\"repo\": \"$DID\", \"collection\": \"app.bsky.feed.post\", \"record\": {\"\\$type\": \"app.bsky.feed.post\", \"text\": $POST_JSON, \"createdAt\": \"$NOW\"}}"
|
|
83
|
-
'
|
|
84
|
-
```
|
|
85
|
-
|
|
86
|
-
Replace `Your post content here` with the user's post text.
|
|
87
|
-
|
|
88
|
-
If the response contains `"uri"`, the post succeeded. Otherwise report the error.
|
|
89
|
-
|
|
90
|
-
## Posting from a news source
|
|
91
|
-
|
|
92
|
-
When the user wants to post **news** or **content from a URL**:
|
|
93
|
-
|
|
94
|
-
1. **Fetch the content** (e.g. with `curl -s <URL>` or read a file).
|
|
95
|
-
2. **Summarize** if the content is long — Bluesky posts are max 300 characters. Write a short headline or summary.
|
|
96
|
-
3. **Post** using the flow above. Optionally append the source URL if it fits within 300 chars.
|
|
97
|
-
|
|
98
|
-
Example for "post news from https://example.com/article":
|
|
99
|
-
- Fetch the page, extract title/lead.
|
|
100
|
-
- Build post: "Headline here — https://example.com/article"
|
|
101
|
-
- Post via the createRecord flow above.
|
|
102
|
-
|
|
103
|
-
## Character limit
|
|
104
|
-
|
|
105
|
-
Bluesky posts are limited to 300 characters. If the user's text or your summary exceeds that, truncate or split into multiple posts (thread). For a thread, create each post separately; the API does not natively support threads — you would need to reference the previous post's URI if linking them (advanced).
|
|
106
|
-
|
|
107
|
-
## Notes
|
|
108
|
-
|
|
109
|
-
- Use an **app password**, not the main account password. Create at bsky.app → Settings → App Passwords.
|
|
110
|
-
- `bsky.social` is the default PDS; custom PDS users would need a different host.
|
|
111
|
-
- Do not embed real credentials in the skill or in replies — use env or config.
|
|
46
|
+
Official docs: https://docs.bsky.app/docs/api/atproto/
|
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: calendar
|
|
3
|
+
description: Use Google Calendar via the Portal. When the user asks to create a calendar event, add to calendar, list events, or check calendar, use this skill with list_integrations_connections (provider calendar) and run_command + curl. Do not use Apple Calendar, osascript, or local calendar apps—use Google Calendar via the Portal.
|
|
4
|
+
metadata:
|
|
5
|
+
{
|
|
6
|
+
"sulala": {
|
|
7
|
+
"emoji": "📅",
|
|
8
|
+
"requires": { "bins": ["curl"] }
|
|
9
|
+
}
|
|
10
|
+
}
|
|
11
|
+
---
|
|
12
|
+
|
|
13
|
+
# Google Calendar
|
|
14
|
+
|
|
15
|
+
Use **list_integrations_connections** with `provider: "calendar"`, then **get_connection_token** to get an OAuth token (do not curl the portal from run_command—use the tool). Then call Calendar with that token.
|
|
16
|
+
|
|
17
|
+
1. **list_integrations_connections** with `provider: "calendar"` → get `connection_id`.
|
|
18
|
+
2. **get_connection_token** with that `connection_id` → returns `accessToken` (runs server-side).
|
|
19
|
+
3. **run_command (curl)** — call Calendar APIs with `Authorization: Bearer <accessToken>` for all requests below.
|
|
20
|
+
|
|
21
|
+
Add `www.googleapis.com` to **ALLOWED_CURL_HOSTS**.
|
|
22
|
+
|
|
23
|
+
Base URL: `https://www.googleapis.com/calendar/v3`
|
|
24
|
+
|
|
25
|
+
---
|
|
26
|
+
|
|
27
|
+
## List calendars
|
|
28
|
+
|
|
29
|
+
`GET https://www.googleapis.com/calendar/v3/users/me/calendarList`. Use `items[].id` (e.g. `primary`) for listing events.
|
|
30
|
+
|
|
31
|
+
---
|
|
32
|
+
|
|
33
|
+
## List events
|
|
34
|
+
|
|
35
|
+
`GET https://www.googleapis.com/calendar/v3/calendars/<calendarId>/events?timeMin=<ISO8601>&timeMax=<ISO8601>&maxResults=20&singleEvents=true`. `calendarId` is often `primary`.
|
|
36
|
+
|
|
37
|
+
---
|
|
38
|
+
|
|
39
|
+
## Create event
|
|
40
|
+
|
|
41
|
+
**Use this for all "create a calendar event" or "add to calendar" requests.**
|
|
42
|
+
|
|
43
|
+
`POST https://www.googleapis.com/calendar/v3/calendars/primary/events` with `Content-Type: application/json`, body:
|
|
44
|
+
|
|
45
|
+
`{"summary": "Event title", "description": "Optional description", "start": {"dateTime": "2025-03-04T21:00:00", "timeZone": "America/New_York"}, "end": {"dateTime": "2025-03-04T21:30:00", "timeZone": "America/New_York"}}`
|
|
46
|
+
|
|
47
|
+
- Example for "gym at 9 PM" today: use today's date, start 21:00, end 21:30 (or 22:00) in the user's timezone.
|
|
48
|
+
- All-day: use `"start": {"date": "2025-03-15"}`, `"end": {"date": "2025-03-16"}`.
|
|
49
|
+
- Times in ISO8601 with timezone (e.g. `America/New_York` or `UTC`).
|
|
50
|
+
|
|
51
|
+
---
|
|
52
|
+
|
|
53
|
+
## Update event
|
|
54
|
+
|
|
55
|
+
`PUT https://www.googleapis.com/calendar/v3/calendars/<calendarId>/events/<eventId>` with same body shape as create.
|
|
56
|
+
|
|
57
|
+
---
|
|
58
|
+
|
|
59
|
+
## Delete event
|
|
60
|
+
|
|
61
|
+
`DELETE https://www.googleapis.com/calendar/v3/calendars/<calendarId>/events/<eventId>`.
|
|
62
|
+
|
|
63
|
+
Requirements: **PORTAL_GATEWAY_URL**, **PORTAL_API_KEY**; user must connect Google Calendar in the Portal or dashboard Integrations.
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: country-info
|
|
3
|
+
description: Provides small info about various countries. Use when the user asks for information about a specific country.
|
|
4
|
+
---
|
|
5
|
+
# Country Info
|
|
6
|
+
|
|
7
|
+
Use when the user asks for small information about a country, such as facts, culture, and geography.
|
|
8
|
+
|
|
9
|
+
## How to use
|
|
10
|
+
- Ask for specific details about a country (e.g., "Tell me about Canada").
|
|
11
|
+
|
|
12
|
+
## Limits
|
|
13
|
+
- The information provided is brief and may not cover all aspects of the country.
|
|
@@ -0,0 +1,128 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: create-skill
|
|
3
|
+
description: Create a new Sulala skill when the user asks for one. Use write_file to create the .md file. Use when the user asks to create, add, or write a skill.
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
# Create Sulala Skill
|
|
7
|
+
|
|
8
|
+
When the user asks to create a skill (e.g. "create a skill for X", "add a skill that does Y"), create it using **write_file**. Infer the skill from the request; ask only if critical details are missing.
|
|
9
|
+
|
|
10
|
+
## Skill format
|
|
11
|
+
|
|
12
|
+
Every Sulala skill is a `.md` file with YAML frontmatter and a markdown body:
|
|
13
|
+
|
|
14
|
+
```markdown
|
|
15
|
+
---
|
|
16
|
+
name: slug-or-name
|
|
17
|
+
description: One-line summary of when to use this skill. Include trigger terms.
|
|
18
|
+
---
|
|
19
|
+
# Skill Title
|
|
20
|
+
|
|
21
|
+
Use when the user asks for X, Y, or Z.
|
|
22
|
+
|
|
23
|
+
## How to use
|
|
24
|
+
- Commands, steps, or run_command examples
|
|
25
|
+
## Limits
|
|
26
|
+
- What not to do
|
|
27
|
+
```
|
|
28
|
+
|
|
29
|
+
**Required frontmatter:** `name` (slug, lowercase-hyphens), `description` (third person, includes WHAT and WHEN).
|
|
30
|
+
|
|
31
|
+
**Required when the skill uses run_command or external APIs:** `metadata` with `sulala.requires`:
|
|
32
|
+
- **bins** — list of CLI tools (e.g. `["curl", "jq"]`). Add these to ALLOWED_BINARIES. Always include if the skill uses run_command.
|
|
33
|
+
- **env** — list of required env var names for API keys (e.g. `["TMDB_ACCESS_TOKEN"]`). Users configure these in Skills config.
|
|
34
|
+
|
|
35
|
+
```yaml
|
|
36
|
+
metadata:
|
|
37
|
+
{
|
|
38
|
+
"sulala": {
|
|
39
|
+
"requires": {
|
|
40
|
+
"bins": ["curl"],
|
|
41
|
+
"env": ["TMDB_ACCESS_TOKEN"]
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
```
|
|
46
|
+
|
|
47
|
+
## Where to write
|
|
48
|
+
|
|
49
|
+
Use the **SKILL.md flow** (direct skill directory, not packaged .skill):
|
|
50
|
+
|
|
51
|
+
1. **Use the path from the Workspace section:** In "## Context" → **## Workspace**, the prompt gives **Your skill output directory**. Use that path with write_file: `<that-directory>/<slug>/SKILL.md`. It is already resolved for the current OS. Do **not** use `~` or `$HOME`; the tool does not expand them and would create a literal folder under the project.
|
|
52
|
+
2. Add `SKILL.md` with YAML frontmatter (`name`, `description`) and instructions.
|
|
53
|
+
3. Add any `scripts/`, `references/`, or `assets/` under that folder if needed.
|
|
54
|
+
4. Do **not** create skills under the project folder (e.g. `context/`) — they will be overwritten on project updates.
|
|
55
|
+
|
|
56
|
+
If write_file cannot write to the path from the Workspace section (e.g. permission or workspace root restriction), tell the user: "Create the skill at [that path], then refresh skills or restart the gateway."
|
|
57
|
+
|
|
58
|
+
## Steps
|
|
59
|
+
|
|
60
|
+
1. Infer purpose and name from the user's request.
|
|
61
|
+
2. Draft frontmatter (name, description) and body (when to use, how to use, limits).
|
|
62
|
+
3. Call **write_file** with `path` = the skill output directory from **## Workspace** + `/<slug>/SKILL.md` (e.g. `/Users/you/.sulala/workspace/skills/<slug>/SKILL.md`). Create the directory if needed.
|
|
63
|
+
4. Confirm: "Created skill at [path]. Refresh skills or restart the gateway to load it."
|
|
64
|
+
|
|
65
|
+
## Example (API skill with metadata)
|
|
66
|
+
|
|
67
|
+
User: "Create a skill that fetches movie data from TMDB"
|
|
68
|
+
|
|
69
|
+
Include **metadata** with `bins` (curl) and `env` (API key). Use write_file with the path from **## Workspace** + `/fetch-movie/SKILL.md`:
|
|
70
|
+
|
|
71
|
+
```markdown
|
|
72
|
+
---
|
|
73
|
+
name: fetch-movie
|
|
74
|
+
description: Fetch movies using TMDB API via curl. Use when the user asks for movie details or to search movies by title.
|
|
75
|
+
metadata:
|
|
76
|
+
{
|
|
77
|
+
"sulala": {
|
|
78
|
+
"requires": {
|
|
79
|
+
"bins": ["curl"],
|
|
80
|
+
"env": ["TMDB_ACCESS_TOKEN"]
|
|
81
|
+
}
|
|
82
|
+
}
|
|
83
|
+
}
|
|
84
|
+
---
|
|
85
|
+
|
|
86
|
+
# Fetch Movie
|
|
87
|
+
|
|
88
|
+
Use **run_command** with `curl` to get movie data from The Movie Database (TMDB) API. Add `curl` to ALLOWED_BINARIES. Store your TMDB API token in Skills config as `TMDB_ACCESS_TOKEN`.
|
|
89
|
+
|
|
90
|
+
## TMDB Search API
|
|
91
|
+
|
|
92
|
+
- **Search:** `curl -s -H "Authorization: Bearer $TMDB_ACCESS_TOKEN" "https://api.themoviedb.org/3/search/movie?query=QUERY"`
|
|
93
|
+
|
|
94
|
+
## Limits
|
|
95
|
+
|
|
96
|
+
- Do not expose the token in responses. Use the env var in run_command.
|
|
97
|
+
```
|
|
98
|
+
|
|
99
|
+
## Example (stock price)
|
|
100
|
+
|
|
101
|
+
User: "Create a skill that fetches stock prices"
|
|
102
|
+
|
|
103
|
+
Use write_file with the path from **## Workspace** + `/stock-price/SKILL.md`:
|
|
104
|
+
|
|
105
|
+
```markdown
|
|
106
|
+
---
|
|
107
|
+
name: stock-price
|
|
108
|
+
description: Fetches stock prices via API. Use when the user asks for stock quotes, share price, or market data.
|
|
109
|
+
---
|
|
110
|
+
|
|
111
|
+
# Stock Price
|
|
112
|
+
|
|
113
|
+
Use **run_command** with `curl` to call a stock API. Add `curl` to ALLOWED_BINARIES.
|
|
114
|
+
|
|
115
|
+
## Alpha Vantage (requires API key)
|
|
116
|
+
|
|
117
|
+
curl -s "https://www.alphavantage.co/query?function=GLOBAL_QUOTE&symbol=AAPL&apikey=$ALPHA_VANTAGE_API_KEY"
|
|
118
|
+
|
|
119
|
+
## Limits
|
|
120
|
+
|
|
121
|
+
- Do not share API keys in responses.
|
|
122
|
+
```
|
|
123
|
+
|
|
124
|
+
## Tips
|
|
125
|
+
|
|
126
|
+
- Description: third person, specific, include trigger terms. Example: "Fetches weather for a city. Use when the user asks for weather, temperature, or forecast."
|
|
127
|
+
- Body: concise; if the skill uses run_command, say which binaries and add to ALLOWED_BINARIES.
|
|
128
|
+
- Slug: lowercase, hyphens only (e.g. `my-new-skill`).
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: discord
|
|
3
|
+
description: Use Discord (servers, channels, send messages) via Settings → Channels. Do not use list_integrations_connections for Discord—it only lists OAuth apps. Use discord_list_guilds, discord_list_channels, and discord_send_message (token from Settings → Channels).
|
|
4
|
+
metadata:
|
|
5
|
+
{
|
|
6
|
+
"sulala": {
|
|
7
|
+
"emoji": "🎮",
|
|
8
|
+
"requires": { "env": ["DISCORD_BOT_TOKEN"] }
|
|
9
|
+
}
|
|
10
|
+
}
|
|
11
|
+
---
|
|
12
|
+
|
|
13
|
+
# Discord
|
|
14
|
+
|
|
15
|
+
Discord is configured in **Settings → Channels (Discord)** or via **DISCORD_BOT_TOKEN** in the agent env. **Do not call list_integrations_connections for Discord**—that tool only returns OAuth connections (Gmail, Slack, etc.). Discord uses a bot token, not OAuth.
|
|
16
|
+
|
|
17
|
+
Use the dedicated tools (they use the token from Settings):
|
|
18
|
+
- **discord_list_guilds** — list servers (guilds) the bot is in. Returns guild id and name.
|
|
19
|
+
- **discord_list_channels** — list channels in a guild. Requires guild_id from discord_list_guilds. Returns channel id, name, type (0=text, 2=voice, 4=category).
|
|
20
|
+
- **discord_send_message** — send a message to a channel. Requires channel_id and content (max 2000 chars).
|
|
21
|
+
|
|
22
|
+
---
|
|
23
|
+
|
|
24
|
+
## Flow
|
|
25
|
+
|
|
26
|
+
1. **discord_list_guilds** — get server (guild) ids and names.
|
|
27
|
+
2. **discord_list_channels** with `guild_id` — get channel ids (type 0 = text channel).
|
|
28
|
+
3. **discord_send_message** with `channel_id` and `content` — send the message.
|
|
29
|
+
|
|
30
|
+
Requirements: Bot must be added to the server and have permissions to read channels and send messages.
|
package/context/docs.md
ADDED
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: docs
|
|
3
|
+
description: Use Google Docs via the Portal. When the user asks to list, create, or read Google Docs, use this skill with list_integrations_connections (provider docs) and run_command + curl.
|
|
4
|
+
metadata:
|
|
5
|
+
{
|
|
6
|
+
"sulala": {
|
|
7
|
+
"emoji": "📄",
|
|
8
|
+
"requires": { "bins": ["curl"] }
|
|
9
|
+
}
|
|
10
|
+
}
|
|
11
|
+
---
|
|
12
|
+
|
|
13
|
+
# Google Docs
|
|
14
|
+
|
|
15
|
+
Use **list_integrations_connections** with `provider: "docs"`, then **get_connection_token** (do not curl the portal). Then call the API with that token.
|
|
16
|
+
|
|
17
|
+
1. **list_integrations_connections** with `provider: "docs"` → get `connection_id`.
|
|
18
|
+
2. **get_connection_token** with that `connection_id` → returns `accessToken`.
|
|
19
|
+
3. **run_command (curl)** with `Authorization: Bearer <accessToken>` for all requests.
|
|
20
|
+
|
|
21
|
+
Add `www.googleapis.com` to **ALLOWED_CURL_HOSTS**.
|
|
22
|
+
|
|
23
|
+
---
|
|
24
|
+
|
|
25
|
+
## List / create / read
|
|
26
|
+
|
|
27
|
+
List and create Docs via the Drive API (mimeType `application/vnd.google-apps.document`). Export to read: `GET https://www.googleapis.com/drive/v3/files/<id>/export?mimeType=text/plain` with `Authorization: Bearer <accessToken>`.
|
|
28
|
+
|
|
29
|
+
Requirements: **PORTAL_GATEWAY_URL**, **PORTAL_API_KEY**; user must connect Google Docs in the Portal or dashboard Integrations.
|