crewly 1.20.35 → 1.20.47
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/config/skills/_common/desktop-guards.sh +485 -0
- package/config/skills/_common/desktop-guards.test.sh +242 -0
- package/config/skills/_common/desktop-perceive.swift +530 -0
- package/config/skills/_common/desktop-presence.swift +343 -0
- package/config/skills/_common/lib.sh +6 -0
- package/config/skills/agent/_common/desktop-guards.sh +4 -0
- package/config/skills/agent/computer-use/SKILL.md +88 -0
- package/config/skills/agent/computer-use/execute.sh +249 -3
- package/config/skills/agent/core/calendar-create/SKILL.md +10 -0
- package/config/skills/agent/core/calendar-create/execute.sh +6 -0
- package/config/skills/agent/core/calendar-list/SKILL.md +10 -0
- package/config/skills/agent/core/calendar-list/execute.sh +6 -0
- package/config/skills/agent/core/docs-read/SKILL.md +10 -0
- package/config/skills/agent/core/docs-read/execute.sh +6 -1
- package/config/skills/agent/core/docs-write/SKILL.md +10 -0
- package/config/skills/agent/core/docs-write/execute.sh +6 -1
- package/config/skills/agent/core/drive-read/SKILL.md +10 -0
- package/config/skills/agent/core/drive-read/execute.sh +6 -1
- package/config/skills/agent/core/drive-search/SKILL.md +10 -0
- package/config/skills/agent/core/drive-search/execute.sh +6 -1
- package/config/skills/agent/core/drive-upload/SKILL.md +10 -0
- package/config/skills/agent/core/drive-upload/execute.sh +6 -1
- package/config/skills/agent/core/gmail-read/SKILL.md +10 -0
- package/config/skills/agent/core/gmail-read/execute.sh +6 -0
- package/config/skills/agent/core/gmail-search/SKILL.md +10 -0
- package/config/skills/agent/core/gmail-search/execute.sh +6 -0
- package/config/skills/agent/core/gmail-send/SKILL.md +10 -0
- package/config/skills/agent/core/gmail-send/execute.sh +6 -0
- package/config/skills/agent/core/sheets-read/SKILL.md +10 -0
- package/config/skills/agent/core/sheets-read/execute.sh +6 -1
- package/config/skills/agent/core/sheets-write/SKILL.md +10 -0
- package/config/skills/agent/core/sheets-write/execute.sh +6 -1
- package/config/skills/agent/core/slides-create/SKILL.md +10 -0
- package/config/skills/agent/core/slides-create/execute.sh +6 -1
- package/config/skills/agent/core/slides-read/SKILL.md +10 -0
- package/config/skills/agent/core/slides-read/execute.sh +6 -1
- package/config/skills/agent/desktop-app-control/SKILL.md +19 -0
- package/config/skills/agent/remote-browser/SKILL.md +19 -0
- package/config/slack-app-manifest.json +16 -9
- package/dist/backend/backend/src/constants.d.ts +18 -4
- package/dist/backend/backend/src/constants.d.ts.map +1 -1
- package/dist/backend/backend/src/constants.js +16 -4
- package/dist/backend/backend/src/constants.js.map +1 -1
- package/dist/backend/backend/src/controllers/desktop/desktop.controller.d.ts +105 -0
- package/dist/backend/backend/src/controllers/desktop/desktop.controller.d.ts.map +1 -0
- package/dist/backend/backend/src/controllers/desktop/desktop.controller.js +278 -0
- package/dist/backend/backend/src/controllers/desktop/desktop.controller.js.map +1 -0
- package/dist/backend/backend/src/controllers/desktop/desktop.routes.d.ts +21 -0
- package/dist/backend/backend/src/controllers/desktop/desktop.routes.d.ts.map +1 -0
- package/dist/backend/backend/src/controllers/desktop/desktop.routes.js +31 -0
- package/dist/backend/backend/src/controllers/desktop/desktop.routes.js.map +1 -0
- package/dist/backend/backend/src/controllers/google/google.controller.d.ts +8 -0
- package/dist/backend/backend/src/controllers/google/google.controller.d.ts.map +1 -1
- package/dist/backend/backend/src/controllers/google/google.controller.js +137 -37
- package/dist/backend/backend/src/controllers/google/google.controller.js.map +1 -1
- package/dist/backend/backend/src/controllers/google/google.routes.d.ts +2 -1
- package/dist/backend/backend/src/controllers/google/google.routes.d.ts.map +1 -1
- package/dist/backend/backend/src/controllers/google/google.routes.js +4 -2
- package/dist/backend/backend/src/controllers/google/google.routes.js.map +1 -1
- package/dist/backend/backend/src/controllers/slack/slack-error.utils.d.ts +46 -0
- package/dist/backend/backend/src/controllers/slack/slack-error.utils.d.ts.map +1 -0
- package/dist/backend/backend/src/controllers/slack/slack-error.utils.js +54 -0
- package/dist/backend/backend/src/controllers/slack/slack-error.utils.js.map +1 -0
- package/dist/backend/backend/src/controllers/slack/slack.controller.d.ts.map +1 -1
- package/dist/backend/backend/src/controllers/slack/slack.controller.js +5 -12
- package/dist/backend/backend/src/controllers/slack/slack.controller.js.map +1 -1
- package/dist/backend/backend/src/routes/api.routes.d.ts.map +1 -1
- package/dist/backend/backend/src/routes/api.routes.js +3 -0
- package/dist/backend/backend/src/routes/api.routes.js.map +1 -1
- package/dist/backend/backend/src/services/cloud/mobile-api-relay.service.d.ts.map +1 -1
- package/dist/backend/backend/src/services/cloud/mobile-api-relay.service.js +9 -0
- package/dist/backend/backend/src/services/cloud/mobile-api-relay.service.js.map +1 -1
- package/dist/backend/backend/src/services/google/google-api.client.d.ts +23 -2
- package/dist/backend/backend/src/services/google/google-api.client.d.ts.map +1 -1
- package/dist/backend/backend/src/services/google/google-api.client.js +5 -2
- package/dist/backend/backend/src/services/google/google-api.client.js.map +1 -1
- package/dist/backend/backend/src/services/google/google-workspace-token.service.d.ts +61 -11
- package/dist/backend/backend/src/services/google/google-workspace-token.service.d.ts.map +1 -1
- package/dist/backend/backend/src/services/google/google-workspace-token.service.js +108 -31
- package/dist/backend/backend/src/services/google/google-workspace-token.service.js.map +1 -1
- package/dist/backend/backend/src/services/slack/slack-team-channel.service.d.ts +24 -0
- package/dist/backend/backend/src/services/slack/slack-team-channel.service.d.ts.map +1 -1
- package/dist/backend/backend/src/services/slack/slack-team-channel.service.js +87 -5
- package/dist/backend/backend/src/services/slack/slack-team-channel.service.js.map +1 -1
- package/dist/backend/backend/src/services/slack/slack.service.d.ts +17 -0
- package/dist/backend/backend/src/services/slack/slack.service.d.ts.map +1 -1
- package/dist/backend/backend/src/services/slack/slack.service.js +32 -0
- package/dist/backend/backend/src/services/slack/slack.service.js.map +1 -1
- package/dist/backend/backend/src/types/slack.types.d.ts +10 -0
- package/dist/backend/backend/src/types/slack.types.d.ts.map +1 -1
- package/dist/backend/backend/src/types/slack.types.js.map +1 -1
- package/dist/backend/backend/src/utils/incomplete-turn.utils.d.ts +1 -1
- package/dist/backend/backend/src/utils/incomplete-turn.utils.d.ts.map +1 -1
- package/dist/backend/backend/src/utils/incomplete-turn.utils.js +4 -0
- package/dist/backend/backend/src/utils/incomplete-turn.utils.js.map +1 -1
- package/dist/backend/build-info.json +2 -2
- package/dist/cli/backend/src/constants.d.ts +18 -4
- package/dist/cli/backend/src/constants.d.ts.map +1 -1
- package/dist/cli/backend/src/constants.js +16 -4
- package/dist/cli/backend/src/constants.js.map +1 -1
- package/dist/cli/backend/src/services/slack/slack-team-channel.service.d.ts +24 -0
- package/dist/cli/backend/src/services/slack/slack-team-channel.service.d.ts.map +1 -1
- package/dist/cli/backend/src/services/slack/slack-team-channel.service.js +87 -5
- package/dist/cli/backend/src/services/slack/slack-team-channel.service.js.map +1 -1
- package/dist/cli/backend/src/services/slack/slack.service.d.ts +17 -0
- package/dist/cli/backend/src/services/slack/slack.service.d.ts.map +1 -1
- package/dist/cli/backend/src/services/slack/slack.service.js +32 -0
- package/dist/cli/backend/src/services/slack/slack.service.js.map +1 -1
- package/dist/cli/backend/src/types/slack.types.d.ts +10 -0
- package/dist/cli/backend/src/types/slack.types.d.ts.map +1 -1
- package/dist/cli/backend/src/types/slack.types.js.map +1 -1
- package/dist/cli/backend/src/utils/incomplete-turn.utils.d.ts +1 -1
- package/dist/cli/backend/src/utils/incomplete-turn.utils.d.ts.map +1 -1
- package/dist/cli/backend/src/utils/incomplete-turn.utils.js +4 -0
- package/dist/cli/backend/src/utils/incomplete-turn.utils.js.map +1 -1
- package/frontend/dist/assets/{index-e079a375.js → index-e7785269.js} +267 -267
- package/frontend/dist/index.html +1 -1
- package/package.json +1 -1
- package/packages/crewly-agent/src/eval/desktop/desktop-tasks.test.ts +96 -0
- package/packages/crewly-agent/src/eval/desktop/desktop-tasks.ts +226 -0
- package/packages/crewly-agent/src/runtime/agent-runner.service.test.ts +10 -1
- package/packages/crewly-agent/src/runtime/agent-runner.service.ts +199 -4
- package/packages/crewly-agent/src/runtime/computer.tool.test.ts +219 -0
- package/packages/crewly-agent/src/runtime/computer.tool.ts +405 -0
- package/packages/crewly-agent/src/runtime/desktop-checkpoint.test.ts +136 -0
- package/packages/crewly-agent/src/runtime/desktop-checkpoint.ts +231 -0
- package/packages/crewly-agent/src/runtime/desktop-recovery.test.ts +100 -0
- package/packages/crewly-agent/src/runtime/desktop-recovery.ts +195 -0
- package/packages/crewly-agent/src/runtime/desktop-task-runtime.test.ts +251 -0
- package/packages/crewly-agent/src/runtime/desktop-task-runtime.ts +423 -0
- package/packages/crewly-agent/src/runtime/desktop-task.tool.test.ts +218 -0
- package/packages/crewly-agent/src/runtime/desktop-task.tool.ts +343 -0
- package/packages/crewly-agent/src/runtime/text-tool-calls.test.ts +144 -0
- package/packages/crewly-agent/src/runtime/text-tool-calls.ts +316 -0
- package/packages/crewly-agent/src/runtime/text-tool-salvage.test.ts +190 -0
- package/packages/crewly-agent/src/runtime/tool-registry.test.ts +17 -0
- package/packages/crewly-agent/src/runtime/tool-registry.ts +54 -0
- package/packages/crewly-agent/src/runtime/types.ts +16 -1
- package/config/skills/agent/vnc-browser/SKILL.md +0 -140
|
@@ -19,6 +19,8 @@ import { KEY_EXTRACTION_BLOCKED_COMMANDS, PromptGuardService } from './prompt-gu
|
|
|
19
19
|
import { EnvIsolationService } from './env-isolation.service.js';
|
|
20
20
|
import { OutputFilterService } from './output-filter.service.js';
|
|
21
21
|
import { createWebSearchTool } from './web-search.tool.js';
|
|
22
|
+
import { createComputerTool } from './computer.tool.js';
|
|
23
|
+
import { createDesktopTaskTool } from './desktop-task.tool.js';
|
|
22
24
|
|
|
23
25
|
/** TTL for delegation idle event subscriptions (minutes) */
|
|
24
26
|
const DELEGATION_SUBSCRIPTION_TTL_MINUTES = 120;
|
|
@@ -48,6 +50,15 @@ const BLOCKED_COMMAND_PATTERNS: RegExp[] = [
|
|
|
48
50
|
/\bdd\s+.*of=\/dev\//i, // dd of=/dev/...
|
|
49
51
|
/\blaunchctl\b/i,
|
|
50
52
|
/\bsystemctl\b/i,
|
|
53
|
+
// Desktop-control guards, made untamperable from inside an agent's own
|
|
54
|
+
// command. The computer-use skill refuses destructive key combos unless
|
|
55
|
+
// CREWLY_DESKTOP_ALLOW_DESTRUCTIVE is set, and stops entirely while
|
|
56
|
+
// ~/.crewly/desktop.stop exists — both of which an agent could otherwise
|
|
57
|
+
// simply set or delete in the same shell line, making the guard theatre.
|
|
58
|
+
// The owner can still set the variable in the agent's environment, which
|
|
59
|
+
// is the intended escape hatch (2026-09-20).
|
|
60
|
+
/\bCREWLY_DESKTOP_ALLOW_DESTRUCTIVE\b/i,
|
|
61
|
+
/\.crewly\/desktop\.(stop|lock)\b/i,
|
|
51
62
|
// Key extraction protection (from prompt-guard.service.ts)
|
|
52
63
|
...KEY_EXTRACTION_BLOCKED_COMMANDS,
|
|
53
64
|
];
|
|
@@ -557,6 +568,11 @@ export const TOOL_SENSITIVITY: Record<string, ToolSensitivity> = {
|
|
|
557
568
|
handoff_task: 'sensitive',
|
|
558
569
|
// Cloud-backed web search
|
|
559
570
|
web_search: 'safe',
|
|
571
|
+
// Desktop control: it moves the owner's real mouse and keyboard.
|
|
572
|
+
computer: 'destructive',
|
|
573
|
+
// The task layer only plans and verifies; the acting happens through
|
|
574
|
+
// `computer`, which carries the destructive rating.
|
|
575
|
+
desktop_task: 'sensitive',
|
|
560
576
|
};
|
|
561
577
|
|
|
562
578
|
/**
|
|
@@ -700,6 +716,21 @@ function sanitizeArgs(args: Record<string, unknown>): Record<string, unknown> {
|
|
|
700
716
|
* @param callbacks - Optional callbacks for compaction and audit logging
|
|
701
717
|
* @returns Object of named tools ready to pass to generateText
|
|
702
718
|
*/
|
|
719
|
+
/**
|
|
720
|
+
* Translate a skill-shaped payload into `computer` tool arguments.
|
|
721
|
+
*
|
|
722
|
+
* The task layer asks in the skill's vocabulary (`snapshot`, `ocr`); the tool
|
|
723
|
+
* speaks the Anthropic-aligned one. One small map here beats teaching the
|
|
724
|
+
* task layer a second vocabulary it has no other use for.
|
|
725
|
+
*
|
|
726
|
+
* @param input - Skill payload
|
|
727
|
+
* @returns Arguments for the computer tool
|
|
728
|
+
*/
|
|
729
|
+
function desktopActionToToolArgs(input: Record<string, unknown>): Record<string, unknown> {
|
|
730
|
+
const action = String(input['action'] ?? '');
|
|
731
|
+
return { action, ...(input['app'] ? { app: input['app'] } : {}) };
|
|
732
|
+
}
|
|
733
|
+
|
|
703
734
|
export function createTools(client: CrewlyApiClient, sessionName: string, projectPath?: string, callbacks?: ToolCallbacks, conversationId?: string, slackContext?: { channelId: string; threadTs?: string }, mcpTools?: Record<string, ToolDefinition>): Record<string, ToolDefinition> {
|
|
704
735
|
// Slack rate-limiting state: throttle messages within a 3-second window
|
|
705
736
|
let lastSlackSendMs = 0;
|
|
@@ -2036,6 +2067,29 @@ export function createTools(client: CrewlyApiClient, sessionName: string, projec
|
|
|
2036
2067
|
// ===== Cloud-Backed Web Search =====
|
|
2037
2068
|
|
|
2038
2069
|
web_search: createWebSearchTool(),
|
|
2070
|
+
// Desktop control. Every action goes through the computer-use skill, so
|
|
2071
|
+
// the safety rails live in one place for every runtime rather than being
|
|
2072
|
+
// reimplemented — and kept in step — here.
|
|
2073
|
+
computer: createComputerTool(),
|
|
2074
|
+
// The layer above it: subgoals whose completion is decided by a
|
|
2075
|
+
// checkpoint rather than by the agent's own account of what happened.
|
|
2076
|
+
// It plans and verifies; the acting still goes through `computer`, and
|
|
2077
|
+
// so through the one set of safety rails.
|
|
2078
|
+
desktop_task: createDesktopTaskTool(sessionName, {
|
|
2079
|
+
perceive: async (input) => {
|
|
2080
|
+
const computer = createComputerTool();
|
|
2081
|
+
const result = await computer.execute(desktopActionToToolArgs(input));
|
|
2082
|
+
return (result ?? {}) as Record<string, unknown>;
|
|
2083
|
+
},
|
|
2084
|
+
...(callbacks?.onEnqueueApproval
|
|
2085
|
+
? {
|
|
2086
|
+
approvals: {
|
|
2087
|
+
enqueue: (_session: string, tool: string, sensitivity: ToolSensitivity, args: Record<string, unknown>) =>
|
|
2088
|
+
({ id: callbacks.onEnqueueApproval!(tool, sensitivity, args).approvalId }) as never,
|
|
2089
|
+
},
|
|
2090
|
+
}
|
|
2091
|
+
: {}),
|
|
2092
|
+
}),
|
|
2039
2093
|
};
|
|
2040
2094
|
|
|
2041
2095
|
// Apply sensitivity classifications and audit wrapping
|
|
@@ -220,7 +220,16 @@ export type IncompleteReason =
|
|
|
220
220
|
/** Hit `maxSteps` with work still outstanding. */
|
|
221
221
|
| 'steps-exhausted'
|
|
222
222
|
/** Provider refused on content grounds. */
|
|
223
|
-
| 'content-filter'
|
|
223
|
+
| 'content-filter'
|
|
224
|
+
/**
|
|
225
|
+
* A desktop task ended with subgoals whose checkpoints never held.
|
|
226
|
+
*
|
|
227
|
+
* The turn itself may have finished perfectly well — the model stopped when
|
|
228
|
+
* it meant to. That is precisely the case worth catching: an agent that
|
|
229
|
+
* believes it saved the file and says so is indistinguishable, from the
|
|
230
|
+
* outside, from one that did.
|
|
231
|
+
*/
|
|
232
|
+
| 'desktop-unverified';
|
|
224
233
|
|
|
225
234
|
/** Describes a turn that ended early. */
|
|
226
235
|
export interface IncompleteRun {
|
|
@@ -556,6 +565,12 @@ export const CREWLY_AGENT_DEFAULTS = {
|
|
|
556
565
|
MAX_CONTINUATIONS: 3,
|
|
557
566
|
/** Re-runs allowed after the provider ended a turn abnormally. */
|
|
558
567
|
MAX_ABNORMAL_RETRIES: 1,
|
|
568
|
+
/** Rounds allowed for executing tool calls a model wrote as text instead of calling. */
|
|
569
|
+
MAX_TEXT_TOOL_SALVAGES: 3,
|
|
570
|
+
/** Tool calls executed per salvage round, so one confused turn cannot fan out. */
|
|
571
|
+
MAX_SALVAGED_CALLS_PER_ROUND: 5,
|
|
572
|
+
/** Characters of a salvaged tool result fed back to the model. */
|
|
573
|
+
SALVAGED_RESULT_MAX_CHARS: 4000,
|
|
559
574
|
/** Maximum tool calls allowed per single response to prevent polling dead-loops */
|
|
560
575
|
MAX_TOOL_CALLS_PER_RESPONSE: 15,
|
|
561
576
|
/** Consecutive identical tool calls before aborting (loop detection) */
|
|
@@ -1,140 +0,0 @@
|
|
|
1
|
-
---
|
|
2
|
-
name: VNC Remote Browser Access
|
|
3
|
-
description: Launch a virtual display with VNC + noVNC + cloudflared for remote browser viewing and control. Enables human-in-the-loop login/verification when Playwright needs manual interaction.
|
|
4
|
-
version: 1.0.0
|
|
5
|
-
category: browser
|
|
6
|
-
skillType: claude-skill
|
|
7
|
-
assignableRoles:
|
|
8
|
-
- developer
|
|
9
|
-
- qa
|
|
10
|
-
- qa-engineer
|
|
11
|
-
- fullstack-dev
|
|
12
|
-
- backend-developer
|
|
13
|
-
- frontend-developer
|
|
14
|
-
- generalist
|
|
15
|
-
triggers:
|
|
16
|
-
- vnc
|
|
17
|
-
- remote browser
|
|
18
|
-
- browser login
|
|
19
|
-
- manual login
|
|
20
|
-
- human verification
|
|
21
|
-
- captcha
|
|
22
|
-
- 2fa
|
|
23
|
-
- playwright headful
|
|
24
|
-
tags:
|
|
25
|
-
- vnc
|
|
26
|
-
- browser
|
|
27
|
-
- remote-access
|
|
28
|
-
- playwright
|
|
29
|
-
- noVNC
|
|
30
|
-
- cloudflared
|
|
31
|
-
execution:
|
|
32
|
-
type: script
|
|
33
|
-
script:
|
|
34
|
-
file: execute.sh
|
|
35
|
-
interpreter: bash
|
|
36
|
-
timeoutMs: 60000
|
|
37
|
-
---
|
|
38
|
-
|
|
39
|
-
# VNC Remote Browser Access
|
|
40
|
-
|
|
41
|
-
Bridge macOS Screen Sharing to a public HTTPS URL via noVNC + cloudflared. This lets humans remotely view and control the Mac desktop (and any browser on it) from anywhere — essential when Playwright hits a login page, CAPTCHA, or 2FA prompt.
|
|
42
|
-
|
|
43
|
-
The stack: **macOS Screen Sharing** (built-in VNC, port 5900) → **websockify + noVNC** (web client, port 6080) → **cloudflared** (public HTTPS tunnel).
|
|
44
|
-
|
|
45
|
-
## Prerequisites
|
|
46
|
-
|
|
47
|
-
**macOS Screen Sharing must be enabled manually** before using this skill:
|
|
48
|
-
|
|
49
|
-
1. Open **System Settings** → **General** → **Sharing**
|
|
50
|
-
2. Turn on **Screen Sharing**
|
|
51
|
-
3. (Optional) Set a VNC password under Screen Sharing options
|
|
52
|
-
|
|
53
|
-
This only needs to be done once. Screen Sharing runs as a system service and persists across reboots.
|
|
54
|
-
|
|
55
|
-
## When to Use
|
|
56
|
-
|
|
57
|
-
- Playwright hits a login page that requires human credentials
|
|
58
|
-
- A website shows a CAPTCHA or anti-bot challenge
|
|
59
|
-
- Two-factor authentication (2FA/MFA) requires a code from the user
|
|
60
|
-
- You need the user to visually verify something in the browser
|
|
61
|
-
- Any browser automation step that needs human-in-the-loop interaction
|
|
62
|
-
|
|
63
|
-
## Parameters
|
|
64
|
-
|
|
65
|
-
| Parameter | Required | Description |
|
|
66
|
-
|-----------|----------|-------------|
|
|
67
|
-
| `action` | Yes | One of: `start`, `stop`, `status`, `get-url` |
|
|
68
|
-
|
|
69
|
-
### Actions
|
|
70
|
-
|
|
71
|
-
- **start** — Check Screen Sharing is on, install deps if needed, launch websockify + noVNC + cloudflared. Returns the public URL.
|
|
72
|
-
- **stop** — Shut down websockify + cloudflared. Does NOT touch Screen Sharing.
|
|
73
|
-
- **status** — Check Screen Sharing (port 5900), websockify, and cloudflared status.
|
|
74
|
-
- **get-url** — Retrieve the cloudflared public URL.
|
|
75
|
-
|
|
76
|
-
## Examples
|
|
77
|
-
|
|
78
|
-
### Start and get the public URL
|
|
79
|
-
|
|
80
|
-
```bash
|
|
81
|
-
bash config/skills/agent/vnc-browser/execute.sh '{"action":"start"}'
|
|
82
|
-
```
|
|
83
|
-
|
|
84
|
-
Output:
|
|
85
|
-
```json
|
|
86
|
-
{
|
|
87
|
-
"success": true,
|
|
88
|
-
"status": "started",
|
|
89
|
-
"publicUrl": "https://abc-xyz.trycloudflare.com/vnc.html?autoconnect=true",
|
|
90
|
-
"localUrl": "http://localhost:6080/vnc.html?autoconnect=true",
|
|
91
|
-
"hint": "Share the publicUrl with the user..."
|
|
92
|
-
}
|
|
93
|
-
```
|
|
94
|
-
|
|
95
|
-
### Send the URL to the user via Slack
|
|
96
|
-
|
|
97
|
-
```bash
|
|
98
|
-
URL=$(bash config/skills/agent/vnc-browser/execute.sh '{"action":"get-url"}' | jq -r '.publicUrl')
|
|
99
|
-
# Then send via reply-slack or report-status
|
|
100
|
-
```
|
|
101
|
-
|
|
102
|
-
### Check status
|
|
103
|
-
|
|
104
|
-
```bash
|
|
105
|
-
bash config/skills/agent/vnc-browser/execute.sh '{"action":"status"}'
|
|
106
|
-
```
|
|
107
|
-
|
|
108
|
-
### Stop when done
|
|
109
|
-
|
|
110
|
-
```bash
|
|
111
|
-
bash config/skills/agent/vnc-browser/execute.sh '{"action":"stop"}'
|
|
112
|
-
```
|
|
113
|
-
|
|
114
|
-
## Workflow
|
|
115
|
-
|
|
116
|
-
1. **Ensure Screen Sharing is on** (one-time setup)
|
|
117
|
-
2. **Start VNC bridge** → get the public URL
|
|
118
|
-
3. **Launch Playwright** in headful mode (the browser appears on the Mac desktop)
|
|
119
|
-
4. **Navigate** to the page requiring human interaction
|
|
120
|
-
5. **Send the public URL** to the user via Slack
|
|
121
|
-
6. **Wait** for the user to complete the manual step (login, CAPTCHA, etc.)
|
|
122
|
-
7. **Continue automation** once the browser is past the manual step
|
|
123
|
-
8. **Stop VNC bridge** when no longer needed
|
|
124
|
-
|
|
125
|
-
## Technical Details
|
|
126
|
-
|
|
127
|
-
- **VNC source**: macOS Screen Sharing on port 5900 (system-managed)
|
|
128
|
-
- **Web client**: noVNC on port 6080 via websockify
|
|
129
|
-
- **Public tunnel**: cloudflared Quick Tunnel (random `*.trycloudflare.com` subdomain)
|
|
130
|
-
- **Security**: Screen Sharing requires macOS user credentials; cloudflared provides HTTPS
|
|
131
|
-
- **Dependencies**: websockify (pip), noVNC (GitHub release), cloudflared (Homebrew) — auto-installed on first run
|
|
132
|
-
- **PID files**: `~/.crewly/vnc/` for websockify and cloudflared lifecycle
|
|
133
|
-
- **Note**: This skill does NOT start or stop macOS Screen Sharing — that is user-managed
|
|
134
|
-
|
|
135
|
-
## Requirements
|
|
136
|
-
|
|
137
|
-
- macOS with Screen Sharing enabled
|
|
138
|
-
- Homebrew installed
|
|
139
|
-
- Python 3 with pip (for websockify)
|
|
140
|
-
- Internet access (for cloudflared tunnel and dependency installation)
|