crewly 1.20.321 → 1.20.323

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.
Files changed (30) hide show
  1. package/config/skills/agent/core/publish-app/SKILL.md +4 -2
  2. package/config/skills/agent/core/publish-app/execute.sh +17 -5
  3. package/config/skills/agent/core/publish-app/execute.test.sh +13 -2
  4. package/config/skills/agent/core/share-file/SKILL.md +73 -0
  5. package/config/skills/agent/core/share-file/execute.sh +184 -0
  6. package/config/skills/agent/core/share-file/execute.test.sh +122 -0
  7. package/dist/backend/backend/src/controllers/apps/apps.controller.d.ts +12 -0
  8. package/dist/backend/backend/src/controllers/apps/apps.controller.d.ts.map +1 -1
  9. package/dist/backend/backend/src/controllers/apps/apps.controller.js +39 -0
  10. package/dist/backend/backend/src/controllers/apps/apps.controller.js.map +1 -1
  11. package/dist/backend/backend/src/controllers/apps/apps.routes.d.ts +5 -1
  12. package/dist/backend/backend/src/controllers/apps/apps.routes.d.ts.map +1 -1
  13. package/dist/backend/backend/src/controllers/apps/apps.routes.js +13 -2
  14. package/dist/backend/backend/src/controllers/apps/apps.routes.js.map +1 -1
  15. package/dist/backend/backend/src/services/ai/prompt-modules/skills-reference.module.d.ts.map +1 -1
  16. package/dist/backend/backend/src/services/ai/prompt-modules/skills-reference.module.js +1 -0
  17. package/dist/backend/backend/src/services/ai/prompt-modules/skills-reference.module.js.map +1 -1
  18. package/dist/backend/backend/src/services/apps/apps-registry.service.d.ts +15 -1
  19. package/dist/backend/backend/src/services/apps/apps-registry.service.d.ts.map +1 -1
  20. package/dist/backend/backend/src/services/apps/apps-registry.service.js +6 -1
  21. package/dist/backend/backend/src/services/apps/apps-registry.service.js.map +1 -1
  22. package/dist/backend/backend/src/services/apps/apps.service.d.ts +22 -3
  23. package/dist/backend/backend/src/services/apps/apps.service.d.ts.map +1 -1
  24. package/dist/backend/backend/src/services/apps/apps.service.js +43 -11
  25. package/dist/backend/backend/src/services/apps/apps.service.js.map +1 -1
  26. package/dist/backend/build-info.json +2 -2
  27. package/dist/cli/backend/src/services/ai/prompt-modules/skills-reference.module.d.ts.map +1 -1
  28. package/dist/cli/backend/src/services/ai/prompt-modules/skills-reference.module.js +1 -0
  29. package/dist/cli/backend/src/services/ai/prompt-modules/skills-reference.module.js.map +1 -1
  30. package/package.json +1 -1
@@ -51,7 +51,8 @@ bash execute.sh --dir ./groceries-app --note "add totals" # next version, s
51
51
  bash execute.sh --html ./timer.html --name "Timer"
52
52
  bash execute.sh --app 28au74d9cj --rollback 2 # back to version 2
53
53
  bash execute.sh --app 28au74d9cj --versions
54
- bash execute.sh --app 28au74d9cj --transfer-to edu-game-milo-13e8d3ca # hand the app to another agent / team
54
+ bash execute.sh --app 28au74d9cj --transfer-to edu-game-milo-13e8d3ca # hand the app to another agent / team (orchestrator or owner only)
55
+ bash execute.sh --app 28au74d9cj --delete # delete (unpublish) a private app your team published
55
56
  bash execute.sh --dir ./daily-brief --owner 'channel:#daily-brief' # publish, and send the owner's comments to a channel
56
57
  bash execute.sh --app 28au74d9cj --owner 'team:Dev' # change only who gets the comments
57
58
  bash execute.sh --list # apps published from this machine
@@ -140,7 +141,8 @@ bash execute.sh --app 28au74d9cj --private # pr
140
141
  collections) still needs a new request the owner approves.
141
142
  - Collaborators: an app another team published is not yours to republish. `--app <id>` from a team that did not publish it is refused (`not_your_app`), including when the owner added your team as a collaborator: collaborators get the app's **data and comments** only (use `app-data`; ask with `app-data --app <id> --request-access`). To change the page itself, ask the publisher or the owner to transfer the app (`--transfer-to`). An owner agent can let an agent on any of the owner's machines work in the app's data: `app-data --app <id> --add-collaborator Rex` (or `Rex@iriss-air` when the name is on several machines).
142
143
  - Owner: `--owner channel:#<name> | team:<name> | agent:<name> | default` sets who the owner's **comments** go to (a channel's members from every team see them; see `app-data` → Owner). It does not change who publishes. Only the app's owner agents may change it (by default the publisher); with a publish the owner is set right after it, and an `ownerError` in the output means only that part failed.
143
- - Transfer: `--app <appId> --transfer-to <session>` hands the app to another agent, e.g. when the owner moves the work to a new team. Only the app's publisher, the lead of its team, the orchestrator or the owner may do it, and the target must be a member of an active team on this machine. Afterwards the new publisher publishes with `--app <appId> --dir <its directory>`, comments and changes go to it, and the old team can no longer publish, roll back or use the app's data. Both agents get a short note.
144
+ - Delete: `--app <appId> --delete` removes an app your team published (URL stops working at once; versions, files and data are removed, so it cannot be undone). Agents may delete only PRIVATE apps; for a public app run `--private` first, or ask the owner. `--list` shows each app's owning team.
145
+ - Transfer: `--app <appId> --transfer-to <session>` hands the app to another agent, e.g. when the owner moves the work to a new team. Only the orchestrator or the owner may do it (team agents and leads ask the orchestrator), every hand-over is recorded (who, when, from, to), versions, data, comments and public/private status stay as they are, and the target must be a member of an active team on this machine. Afterwards the new publisher publishes with `--app <appId> --dir <its directory>`, comments and changes go to it, and the old team can no longer publish, roll back or use the app's data. Both agents get a short note.
144
146
  - Thumbnails: the owner's portal list shows a small screenshot of each app. Publishing (and rollback) takes it automatically in the background when this machine has Chrome or Chromium; `bash execute.sh --app <appId> --refresh-thumbnail` re-takes it now (answers `captured:false` with a reason such as `no_browser` when it cannot).
145
147
  - `--private` is always allowed and takes effect at once. Use it if anything
146
148
  looks wrong (spam, abuse, a leak).
@@ -28,7 +28,8 @@ Usage:
28
28
  bash execute.sh --app <appId> --dir ./my-app # republish to a known app explicitly
29
29
  bash execute.sh --app <appId> --rollback <version> # make an earlier version current
30
30
  bash execute.sh --app <appId> --versions # list versions
31
- bash execute.sh --app <appId> --transfer-to <session> # hand the app to another agent / team
31
+ bash execute.sh --app <appId> --transfer-to <session> # hand the app to another agent / team (orchestrator or owner only)
32
+ bash execute.sh --app <appId> --delete # delete (unpublish) an app your team published (private apps)
32
33
  bash execute.sh --app <appId> --owner 'channel:#daily-brief' # who gets the owner's comments (also with a publish)
33
34
  bash execute.sh --list # apps published from this machine
34
35
  bash execute.sh --app <appId> --share [--ttl-days 7] # fresh one-tap link card to the owner (no publish)
@@ -81,12 +82,14 @@ Options:
81
82
  --unlist-template Take a template off the Marketplace (apps made from it are not affected)
82
83
  --rollback Version number to make current (needs --app)
83
84
  --versions List versions (needs --app)
85
+ --delete Delete (unpublish) the app: its URL stops working and its versions, files and data are removed. Needs --app.
86
+ Only for a PRIVATE app your team published; a public app is the owner's to delete (use --private first or ask the owner).
84
87
  --transfer-to Hand the app to another agent (its session name; it must be on a team on this machine).
85
88
  --owner Who owns the app's comments: channel:#<name>, team:<name>, agent:<name> or default (the publishing agent).
86
89
  With a publish it is set after the publish; alone it needs --app. Only the app's owner agents may change it.
87
- Allowed for the app's publisher, the lead of its team, the orchestrator or the owner.
90
+ Allowed for the orchestrator or the owner only; the transfer is recorded (who, when, from, to).
88
91
  The new publisher publishes with --app <appId> --dir <its directory>; the old team loses access.
89
- --list List apps published from this machine
92
+ --list List apps published from this machine, with the owning team
90
93
  --help | -h Show this help
91
94
  EOF_USAGE
92
95
  }
@@ -104,7 +107,7 @@ call() {
104
107
  printf '%s\n' "$out" | tail -n 1
105
108
  }
106
109
 
107
- DIR=""; FILE=""; NAME=""; APP=""; ENTRY=""; NOTE=""; NOTIFY=false; ROLLBACK=""; TRANSFER_TO=""; VERSIONS=false; LIST=false
110
+ DIR=""; FILE=""; NAME=""; APP=""; ENTRY=""; NOTE=""; NOTIFY=false; ROLLBACK=""; TRANSFER_TO=""; DELETE_APP=false; VERSIONS=false; LIST=false
108
111
  SHARE=false; TTL_DAYS=""; LINKS=false; REVOKE_LINK=""; REVOKE_LINKS=false; OWNER_SPEC=""
109
112
  CAPABILITY=""; CAPABILITY_NOTE=""; CANCEL_CAPABILITY=false
110
113
  PUBLIC=false; PUBLIC_READ=""; PUBLIC_SUBMIT=""; PUBLIC_NOTE=""; CANCEL_PUBLIC=false; PRIVATE=false; REFRESH_THUMB=false
@@ -138,6 +141,7 @@ while [[ $# -gt 0 ]]; do
138
141
  --transfer-to) [ $# -ge 2 ] || error_exit "--transfer-to requires a session name"; TRANSFER_TO="$2"; shift 2 ;;
139
142
  --owner) [ $# -ge 2 ] || error_exit "--owner requires channel:#<name>, team:<name>, agent:<name> or default"; OWNER_SPEC="$2"; shift 2 ;;
140
143
  --list) LIST=true; shift ;;
144
+ --delete) DELETE_APP=true; shift ;;
141
145
  --as-template) AS_TEMPLATE=true; shift ;;
142
146
  --description) [ $# -ge 2 ] || error_exit "--description requires a value"; TPL_DESCRIPTION="$2"; shift 2 ;;
143
147
  --category) [ $# -ge 2 ] || error_exit "--category requires a value"; TPL_CATEGORY="$2"; shift 2 ;;
@@ -200,7 +204,7 @@ fi
200
204
 
201
205
  if $LIST; then
202
206
  RESPONSE=$(call GET "/apps") || { printf '%s\n' "$RESPONSE"; exit 1; }
203
- printf '%s' "$RESPONSE" | jq -c '{success: true, apps: [.data[]? | select(.deleted != true) | {appId, name, url, agent: .agentSession, currentVersion, source}]}'
207
+ printf '%s' "$RESPONSE" | jq -c '{success: true, apps: [.data[]? | select(.deleted != true) | {appId, name, url, agent: .agentSession, team, currentVersion, source}]}'
204
208
  exit 0
205
209
  fi
206
210
 
@@ -300,6 +304,14 @@ if [ -n "$OWNER_SPEC" ] && [ -z "$DIR" ] && [ -z "$FILE" ]; then
300
304
  exit 0
301
305
  fi
302
306
 
307
+ if $DELETE_APP; then
308
+ [ -n "$APP" ] || error_exit "--app is required with --delete"
309
+ if [ -n "$DIR" ] || [ -n "$FILE" ] || [ -n "$TRANSFER_TO" ] || [ -n "$ROLLBACK" ]; then error_exit "--delete cannot be combined with a publish, --transfer-to or --rollback"; fi
310
+ RESPONSE=$(call DELETE "/apps/${APP}") || { printf '%s\n' "$RESPONSE"; exit 1; }
311
+ printf '%s' "$RESPONSE" | jq -c '{success: true, appId: .data.appId, deleted: .data.deleted}'
312
+ exit 0
313
+ fi
314
+
303
315
  if [ -n "$TRANSFER_TO" ]; then
304
316
  [ -n "$APP" ] || error_exit "--app is required with --transfer-to"
305
317
  BODY=$(jq -cn --arg s "$TRANSFER_TO" '{toSession: $s}')
@@ -82,6 +82,10 @@ class H(BaseHTTPRequestHandler):
82
82
  if data.get('owner') == 'channel:#nope':
83
83
  return send(400, {'success': False, 'error': 'validation', 'message': 'No channel "#nope" on this machine.'})
84
84
  return send(200, {'success': True, 'data': {'appId': '28au74d9cj', 'owner': {'kind': 'channel', 'explicit': True, 'name': '#daily-brief'}, 'previous': None}})
85
+ if method == 'DELETE' and self.path == '/api/apps/28au74d9cj':
86
+ return send(200, {'success': True, 'data': {'appId': '28au74d9cj', 'deleted': True}})
87
+ if method == 'DELETE' and self.path == '/api/apps/bcdfghjkmn':
88
+ return send(403, {'success': False, 'error': 'forbidden', 'message': 'This app is public, so only the owner can delete it.'})
85
89
  if self.path == '/api/apps/28au74d9cj/transfer':
86
90
  return send(200, {'success': True, 'data': {'appId': '28au74d9cj', 'name': 'G', 'publisher': data['toSession'], 'previous': 'dev-ella', 'changed': True, 'notified': [data['toSession']]}})
87
91
  if self.path.endswith('/rollback'):
@@ -90,7 +94,7 @@ class H(BaseHTTPRequestHandler):
90
94
  if self.path.endswith('/versions'):
91
95
  return send(200, {'success': True, 'data': [{'version': 2, 'current': True, 'note': None, 'files': 1, 'totalBytes': 9, 'createdAt': 't', 'entry': 'index.html'}]})
92
96
  if self.path == '/api/apps':
93
- return send(200, {'success': True, 'data': [{'appId': 'a', 'name': 'A', 'url': 'u', 'agentSession': 's', 'currentVersion': 1, 'source': '/x'},
97
+ return send(200, {'success': True, 'data': [{'appId': 'a', 'name': 'A', 'url': 'u', 'agentSession': 's', 'team': 'Alpha', 'currentVersion': 1, 'source': '/x'},
94
98
  {'appId': 'b', 'name': 'B', 'deleted': True}]})
95
99
  send(404, {'success': False, 'error': 'not_found'})
96
100
  def do_GET(self): self._reply('GET')
@@ -167,10 +171,17 @@ check "transfer: output" "$OUT" '{"success":true,"appId":"28au74d9cj","publisher
167
171
  check "transfer: request" "$(jq -c '{path, body}' "$STUB_LOG")" '{"path":"/api/apps/28au74d9cj/transfer","body":{"toSession":"edu-game-milo-13e8d3ca"}}'
168
172
  check "transfer needs --app" "$(run_err --transfer-to x | grep -c -- '--app is required with --transfer-to')" "1"
169
173
 
174
+ OUT=$(run --app 28au74d9cj --delete)
175
+ check "delete: output" "$OUT" '{"success":true,"appId":"28au74d9cj","deleted":true}'
176
+ check "delete: request" "$(jq -c '{method, path}' "$STUB_LOG")" '{"method":"DELETE","path":"/api/apps/28au74d9cj"}'
177
+ check "delete needs --app" "$(run_err --delete | grep -c -- '--app is required with --delete')" "1"
178
+ OUT=$(run --app bcdfghjkmn --delete || true)
179
+ check "delete: public app refused" "$(printf '%s' "$OUT" | jq -r '.success, .reason' | tr '\n' ' ')" "false forbidden "
180
+
170
181
  OUT=$(run --app 28au74d9cj --versions)
171
182
  check "versions" "$OUT" '{"success":true,"versions":[{"version":2,"current":true,"note":null,"files":1,"totalBytes":9,"createdAt":"t"}]}'
172
183
  OUT=$(run --list)
173
- check "list hides deleted" "$OUT" '{"success":true,"apps":[{"appId":"a","name":"A","url":"u","agent":"s","currentVersion":1,"source":"/x"}]}'
184
+ check "list hides deleted" "$OUT" '{"success":true,"apps":[{"appId":"a","name":"A","url":"u","agent":"s","team":"Alpha","currentVersion":1,"source":"/x"}]}'
174
185
 
175
186
  OUT=$(run --dir "$APPDIR" --name nologin; true)
176
187
  check "backend error mapped" "$OUT" '{"success":false,"status":409,"reason":"not_logged_in","message":"not signed in","hint":"crewly cloud login"}'
@@ -0,0 +1,73 @@
1
+ ---
2
+ name: Share File
3
+ description: Hand the OWNER a LARGE file you made (an HD video, an archive, up to 4 GB) as a private link they open on their phone, watch right in the page and download. Reverse of request-file. Use instead of Slack, Drive or asking the owner to come to the machine.
4
+ version: 1.0.0
5
+ category: productivity
6
+ skillType: claude-skill
7
+ assignableRoles:
8
+ - "*"
9
+ triggers:
10
+ - share file
11
+ - share the video
12
+ - send the owner a file
13
+ - put the video on a link
14
+ - the video is ready
15
+ - download link for the owner
16
+ - hand the file to the owner
17
+ tags:
18
+ - files
19
+ - owner
20
+ - video
21
+ execution:
22
+ type: script
23
+ script:
24
+ file: execute.sh
25
+ interpreter: bash
26
+ timeoutMs: 7200000
27
+ ---
28
+
29
+ # Share File
30
+
31
+ Gives **the owner**, who is probably on a phone and away from the machine, a
32
+ private link to a big file you made. They open it, **watch or listen right in
33
+ the page** (video, images and audio play inline and can be scrubbed) and tap
34
+ **Download** to save it to the phone. Needs the machine to be signed in to
35
+ Crewly Cloud. The file is streamed from disk (never loaded into memory).
36
+
37
+ ```bash
38
+ bash execute.sh --path out/final.mp4 --note "the HD cut, 1.2 GB"
39
+ bash execute.sh --status # your shared files and their links
40
+ bash execute.sh --status sf_AbCdEfGhIjKlMnOpQrStUv
41
+ bash execute.sh --cancel sf_AbCdEfGhIjKlMnOpQrStUv
42
+ ```
43
+
44
+ Prints, once the upload is finished (big files print `uploaded 10%` ... lines to stderr while it runs):
45
+
46
+ ```json
47
+ {"success":true,"shareId":"sf_…","url":"https://crewlyai.com/portal/files/sf_…","fileName":"final.mp4",
48
+ "sizeBytes":1288490188,"expiresAt":"…","instruction":"Post the url to the owner…"}
49
+ ```
50
+
51
+ ## What to do with it
52
+
53
+ 1. **Post the `url` to the owner** where you are talking to them (the Slack
54
+ thread, the chat), in your own words: what the file is, that they can open it
55
+ on their phone, watch it in the page and download it, and that it stays for
56
+ 7 days. One short message. Do not paste this skill's JSON.
57
+ 2. The owner signs in to their Crewly account if asked. Nobody else can open
58
+ the link. If they say it expired, run the skill again for a fresh link.
59
+ 3. `--cancel` takes a file down you no longer want shared. A mistake (wrong
60
+ file) is a good reason to cancel straight away.
61
+
62
+ ## Rules
63
+
64
+ - `--path` must be a regular file **inside your project directory**: not a
65
+ symlink, not inside Crewly's home. Up to 4 GB. Copy files from elsewhere
66
+ into the project first.
67
+ - `--name` is the name the owner sees and saves it under (default: the file's
68
+ name). `--note` is one line for the owner (up to 500 characters).
69
+ - The account may hold 20 GB / 20 shared files at once; cancel old ones or wait
70
+ for them to expire (7 days after upload).
71
+ - Use for the finished thing the owner wants to see or keep. Not for secrets
72
+ or credentials, and not for files under ~5 MB that fit in a message. To get
73
+ a file FROM the owner, use `request-file`.
@@ -0,0 +1,184 @@
1
+ #!/bin/bash
2
+ # =============================================================================
3
+ # share-file — put a LARGE file you made (an HD video, an archive) on a private
4
+ # link for the OWNER, who watches it in the page and downloads it on the phone.
5
+ # The reverse of request-file (crewly-services apps/SPEC.md section 20).
6
+ #
7
+ # The backend (POST /api/apps/shares) asks Cloud for a presigned upload URL;
8
+ # this script then streams the file straight to object storage with curl -T
9
+ # (from disk, never buffered), and confirms with /complete.
10
+ #
11
+ # Usage:
12
+ # bash execute.sh --path <file> [--name <name>] [--note <text>] [--content-type <type>]
13
+ # bash execute.sh --status [<shareId>]
14
+ # bash execute.sh --cancel <shareId>
15
+ # =============================================================================
16
+ set -euo pipefail
17
+ SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
18
+ source "${SCRIPT_DIR}/../../_common/lib.sh"
19
+
20
+ # Progress lines (one per 10%) are printed for uploads at least this big.
21
+ PROGRESS_MIN_BYTES="${SHARE_FILE_PROGRESS_MIN_BYTES:-52428800}"
22
+
23
+ print_usage() {
24
+ cat <<'EOF_USAGE'
25
+ Usage:
26
+ bash execute.sh --path <file> [--name <name>] [--note <text>] [--content-type <type>]
27
+ bash execute.sh --status [<shareId>]
28
+ bash execute.sh --cancel <shareId>
29
+
30
+ Options:
31
+ --path Regular file inside your project directory, up to 4 GB (required)
32
+ --name Name the owner sees and saves it under (default: the file's name)
33
+ --note One line for the owner (what it is, e.g. "the HD cut, 1.2 GB")
34
+ --content-type MIME type (default: from the extension)
35
+ --status List your shared files (with their links), or show one
36
+ --cancel Take a shared file down (deletes the uploaded file)
37
+ --help | -h Show this help
38
+ EOF_USAGE
39
+ }
40
+
41
+ fail_from() {
42
+ printf '%s' "$1" | jq -c '{success: false, status: (.status // 0), reason: (.details.error // .error // "unknown"), message: (.details.message // .details // "")}' 2>/dev/null \
43
+ || jq -cn --arg r "$1" '{success: false, reason: $r}'
44
+ exit 1
45
+ }
46
+
47
+ call() {
48
+ local out
49
+ out=$(api_call "$@" 2>&1) || { fail_from "$(printf '%s\n' "$out" | tail -n 1)"; }
50
+ printf '%s\n' "$out" | tail -n 1
51
+ }
52
+
53
+ content_type_for() {
54
+ case "$(printf '%s' "${1##*.}" | tr '[:upper:]' '[:lower:]')" in
55
+ mp4|m4v) echo video/mp4 ;;
56
+ mov) echo video/quicktime ;;
57
+ webm) echo video/webm ;;
58
+ mkv) echo video/x-matroska ;;
59
+ mp3) echo audio/mpeg ;;
60
+ wav) echo audio/wav ;;
61
+ m4a) echo audio/mp4 ;;
62
+ png) echo image/png ;;
63
+ jpg|jpeg) echo image/jpeg ;;
64
+ gif) echo image/gif ;;
65
+ webp) echo image/webp ;;
66
+ pdf) echo application/pdf ;;
67
+ zip) echo application/zip ;;
68
+ json) echo application/json ;;
69
+ csv) echo text/csv ;;
70
+ txt|md|log) echo text/plain ;;
71
+ *) echo application/octet-stream ;;
72
+ esac
73
+ }
74
+
75
+ # Regular, non-symlink file inside the project directory, never under Crewly's
76
+ # home. Prints the real path, then the size in bytes.
77
+ check_file() {
78
+ command -v node >/dev/null 2>&1 || error_exit "node is required"
79
+ CF_PATH="$1" CF_PROJECT="${CREWLY_PROJECT_PATH:-$PWD}" node <<'NODE'
80
+ const fs = require('fs'); const path = require('path'); const os = require('os');
81
+ const fail = (m) => { process.stderr.write(JSON.stringify({ error: m }) + '\n'); process.exit(1); };
82
+ const real = (p) => { try { return fs.realpathSync(p); } catch { return null; } };
83
+ const within = (c, p) => c === p || c.startsWith(p.endsWith(path.sep) ? p : p + path.sep);
84
+ const given = process.env.CF_PATH;
85
+ const abs = path.resolve(given);
86
+ let st;
87
+ try { st = fs.lstatSync(abs); } catch { fail(`file not found: ${given}`); }
88
+ if (st.isSymbolicLink()) fail(`${given} is a symbolic link; pass the real file`);
89
+ if (!st.isFile()) fail(`${given} is not a regular file`);
90
+ if (st.size === 0) fail(`${given} is empty`);
91
+ const r = real(abs); const project = real(process.env.CF_PROJECT || process.cwd());
92
+ if (!project) fail('cannot resolve the project directory');
93
+ const homes = [path.join(os.homedir(), '.crewly'), process.env.CREWLY_HOME].filter(Boolean).map((p) => real(p) || path.resolve(p));
94
+ if (homes.some((h) => within(r, h))) fail(`${given} is inside Crewly's home directory`);
95
+ if (!within(r, project)) fail(`${given} is outside your project directory (${project}); copy it there first`);
96
+ process.stdout.write(r + '\n' + st.size + '\n');
97
+ NODE
98
+ }
99
+
100
+ # Stream the file to the presigned URL (curl -T reads from disk). Prints the HTTP
101
+ # status. Big files report progress (one stderr line per 10%) so the run is not silent.
102
+ upload() {
103
+ local file="$1" size="$2" url="$3"; shift 3
104
+ if [ "$size" -ge "$PROGRESS_MIN_BYTES" ]; then
105
+ echo "uploading $(( size / 1048576 )) MB..." >&2
106
+ curl --progress-bar -S -o /dev/null -w '%{http_code}' --connect-timeout 30 --retry 3 --retry-delay 3 -X PUT -T "$file" "$@" "$url" \
107
+ 2> >(tr '\r' '\n' | awk 'BEGIN{last=0} match($0,/[0-9]+\.[0-9]%/){p=int(substr($0,RSTART)+0); if(p>=last+10){last=p-(p%10); printf "uploaded %d%%\n", last > "/dev/stderr"; fflush("/dev/stderr")}}') \
108
+ || printf '000'
109
+ else
110
+ curl -sS -o /dev/null -w '%{http_code}' --connect-timeout 30 --retry 3 --retry-delay 3 -X PUT -T "$file" "$@" "$url" 2>/dev/null || printf '000'
111
+ fi
112
+ }
113
+
114
+ FILE=""; NAME=""; NOTE=""; CTYPE=""; CANCEL=""; STATUS=0; STATUS_ID=""
115
+ while [[ $# -gt 0 ]]; do
116
+ case "$1" in
117
+ --path) [ $# -ge 2 ] || error_exit "--path requires a value"; FILE="$2"; shift 2 ;;
118
+ --name) [ $# -ge 2 ] || error_exit "--name requires a value"; NAME="$2"; shift 2 ;;
119
+ --note) [ $# -ge 2 ] || error_exit "--note requires a value"; NOTE="$2"; shift 2 ;;
120
+ --content-type) [ $# -ge 2 ] || error_exit "--content-type requires a value"; CTYPE="$2"; shift 2 ;;
121
+ --cancel) [ $# -ge 2 ] || error_exit "--cancel requires a share id"; CANCEL="$2"; shift 2 ;;
122
+ --status) STATUS=1; shift
123
+ if [ $# -ge 1 ] && [[ "$1" =~ ^sf_ ]]; then STATUS_ID="$1"; shift; fi ;;
124
+ --help|-h) print_usage; exit 0 ;;
125
+ *) unknown_arg_exit "$1" ;;
126
+ esac
127
+ done
128
+
129
+ ID_RE='^sf_[A-Za-z0-9_-]{22}$'
130
+
131
+ if [ -n "$CANCEL" ]; then
132
+ [[ "$CANCEL" =~ $ID_RE ]] || error_exit "--cancel needs a share id like sf_…"
133
+ RESULT=$(call DELETE "/apps/shares/${CANCEL}") || { printf '%s\n' "$RESULT"; exit 1; }
134
+ jq -cn --arg id "$CANCEL" '{success: true, shareId: $id, cancelled: true}'
135
+ exit 0
136
+ fi
137
+
138
+ if [ "$STATUS" = "1" ]; then
139
+ if [ -n "$STATUS_ID" ]; then
140
+ [[ "$STATUS_ID" =~ $ID_RE ]] || error_exit "--status needs a share id like sf_…"
141
+ RESULT=$(call GET "/apps/shares/${STATUS_ID}") || { printf '%s\n' "$RESULT"; exit 1; }
142
+ printf '%s' "$RESULT" | jq -c '{success: true} + (.data | {shareId, status, goneReason, fileName, sizeBytes, url, expiresAt})'
143
+ else
144
+ RESULT=$(call GET "/apps/shares") || { printf '%s\n' "$RESULT"; exit 1; }
145
+ printf '%s' "$RESULT" | jq -c '{success: true, shares: [.data.shares[]? | {shareId, status, fileName, sizeBytes, url, expiresAt}]}'
146
+ fi
147
+ exit 0
148
+ fi
149
+
150
+ [ -n "$FILE" ] || error_exit "--path is required"
151
+ INFO=$(check_file "$FILE") || { printf '%s\n' "$INFO" >&2; exit 1; }
152
+ REAL=$(printf '%s\n' "$INFO" | sed -n 1p)
153
+ SIZE=$(printf '%s\n' "$INFO" | sed -n 2p)
154
+ [ -n "$NAME" ] || NAME="$(basename "$REAL")"
155
+ [ -n "$CTYPE" ] || CTYPE="$(content_type_for "$REAL")"
156
+
157
+ BODY=$(jq -cn --arg n "$NAME" --argjson s "$SIZE" --arg c "$CTYPE" --arg note "$NOTE" \
158
+ '{fileName: $n, sizeBytes: $s, contentType: $c} + (if $note != "" then {note: $note} else {} end)')
159
+ CREATED=$(call POST "/apps/shares" "$BODY") || { printf '%s\n' "$CREATED"; exit 1; }
160
+ ID=$(printf '%s' "$CREATED" | jq -r '.data.shareId // empty')
161
+ URL=$(printf '%s' "$CREATED" | jq -r '.data.upload.url // empty')
162
+ LINK=$(printf '%s' "$CREATED" | jq -r '.data.url // empty')
163
+ [ -n "$ID" ] && [ -n "$URL" ] || error_exit "Cloud answered without an upload URL"
164
+
165
+ # Headers the presigned URL was signed with. The signed URL itself is never printed or logged.
166
+ HDRS=()
167
+ while IFS= read -r line; do HDRS+=(-H "$line"); done < <(printf '%s' "$CREATED" | jq -r '.data.upload.headers // {} | to_entries[] | "\(.key): \(.value)"')
168
+
169
+ cancel_quietly() { api_call DELETE "/apps/shares/${ID}" >/dev/null 2>&1 || true; }
170
+
171
+ CODE=$(upload "$REAL" "$SIZE" "$URL" ${HDRS[@]+"${HDRS[@]}"})
172
+ if [ "$CODE" != "200" ]; then
173
+ cancel_quietly
174
+ jq -cn --arg c "$CODE" '{success: false, reason: "upload_failed", message: ("The upload to temporary storage failed (HTTP " + $c + "). Nothing was kept; try again.")}'
175
+ exit 1
176
+ fi
177
+
178
+ DONE=$(api_call POST "/apps/shares/${ID}/complete" 2>&1) || { cancel_quietly; fail_from "$(printf '%s\n' "$DONE" | tail -n 1)"; }
179
+ EXPIRES=$(printf '%s\n' "$DONE" | tail -n 1 | jq -r '.data.expiresAt // empty')
180
+ [ -n "$LINK" ] || LINK=$(printf '%s\n' "$DONE" | tail -n 1 | jq -r '.data.url // empty')
181
+
182
+ jq -cn --arg id "$ID" --arg link "$LINK" --arg n "$NAME" --argjson s "$SIZE" --arg e "$EXPIRES" \
183
+ '{success: true, shareId: $id, url: $link, fileName: $n, sizeBytes: $s, expiresAt: $e,
184
+ instruction: "Post the url to the owner where you are talking to them, in your own words: what the file is, that they can open it on their phone, watch it right in the page and download it, and that it stays available for 7 days. One short message; do not paste this JSON. The link is private to their Crewly account. Withdraw it with: bash <skills>/core/share-file/execute.sh --cancel \($id)"}'
@@ -0,0 +1,122 @@
1
+ #!/bin/bash
2
+ # Tests for share-file — run with: bash execute.test.sh
3
+ # A Python stub plays both the Crewly backend and the object storage.
4
+ set -u
5
+ HERE="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
6
+ EXEC="$HERE/execute.sh"
7
+ PASS=0; FAIL=0
8
+ check() { if [ "$2" = "$3" ]; then PASS=$((PASS+1)); else FAIL=$((FAIL+1)); echo "FAIL: $1"; echo " want: $3"; echo " got: $2"; fi; }
9
+
10
+ PORT=18862
11
+ STUB_LOG="$(mktemp)"; STORE="$(mktemp)"; export STUB_LOG STORE STUB_PORT="$PORT"
12
+ python3 -u <<'PY' >/dev/null 2>&1 &
13
+ from http.server import BaseHTTPRequestHandler, HTTPServer
14
+ import json, os
15
+ LOG = os.environ['STUB_LOG']; STORE = os.environ['STORE']; PORT = int(os.environ['STUB_PORT'])
16
+ ID = 'sf_AbCdEfGhIjKlMnOpQrStUv'
17
+ LINK = 'https://crewlyai.com/portal/files/' + ID
18
+ def note(**kw):
19
+ with open(LOG, 'a') as f: f.write(json.dumps(kw) + '\n')
20
+ class H(BaseHTTPRequestHandler):
21
+ def send(self, code, obj):
22
+ self.send_response(code); self.send_header('Content-Type', 'application/json'); self.end_headers(); self.wfile.write(json.dumps(obj).encode())
23
+ def body(self):
24
+ n = int(self.headers.get('Content-Length') or 0); return self.rfile.read(n)
25
+ def do_POST(self):
26
+ b = self.body()
27
+ if self.path == '/api/apps/shares':
28
+ req = json.loads(b); note(op='create', req=req, session=self.headers.get('X-Agent-Session'))
29
+ if req['fileName'] == 'refuse.bin':
30
+ return self.send(413, {'success': False, 'error': 'too_large', 'message': 'A shared file can be at most 4096 MB.'})
31
+ return self.send(201, {'success': True, 'data': {'shareId': ID, 'status': 'pending', 'url': LINK, 'upload': {'url': f'http://127.0.0.1:{PORT}/store/obj?X-Amz-Signature=SECRETSIG', 'method': 'PUT', 'headers': {'Content-Type': req['contentType'], 'x-amz-server-side-encryption': 'AES256'}}}})
32
+ if self.path == f'/api/apps/shares/{ID}/complete':
33
+ note(op='complete'); return self.send(200, {'success': True, 'data': {'shareId': ID, 'status': 'ready', 'url': LINK, 'expiresAt': '2026-10-20T00:00:00.000Z'}})
34
+ self.send(404, {'success': False})
35
+ def do_GET(self):
36
+ note(op='get', path=self.path)
37
+ if self.path == f'/api/apps/shares/{ID}':
38
+ return self.send(200, {'success': True, 'data': {'shareId': ID, 'status': 'ready', 'fileName': 'daily.mp4', 'sizeBytes': 3000000, 'url': LINK, 'expiresAt': '2026-10-20T00:00:00.000Z', 'download': {'url': 'https://secret.example/x'}}})
39
+ if self.path == '/api/apps/shares':
40
+ return self.send(200, {'success': True, 'data': {'shares': [{'shareId': ID, 'status': 'ready', 'fileName': 'daily.mp4', 'sizeBytes': 3000000, 'url': LINK, 'expiresAt': '2026-10-20T00:00:00.000Z', 'from': {'id': 'dev-ella'}}]}})
41
+ self.send(404, {'success': False})
42
+ def do_DELETE(self):
43
+ note(op='cancel', path=self.path); self.send(200, {'success': True, 'data': {'deleted': True}})
44
+ def do_PUT(self):
45
+ b = self.body()
46
+ note(op='put', bytes=len(b), ctype=self.headers.get('Content-Type'), sse=self.headers.get('x-amz-server-side-encryption'), query=self.path.split('?')[-1])
47
+ self.send_response(500 if os.path.exists(STORE + '.fail') else 200); self.end_headers()
48
+ def log_message(self, *a, **k): pass
49
+ HTTPServer(('127.0.0.1', PORT), H).serve_forever()
50
+ PY
51
+ STUB_PID=$!
52
+ disown "$STUB_PID" 2>/dev/null || true
53
+ WORK="$(mktemp -d)"
54
+ trap 'kill "$STUB_PID" >/dev/null 2>&1; rm -rf "$STUB_LOG" "$STORE" "$STORE.fail" "$WORK"' EXIT
55
+ for i in $(seq 1 30); do curl -s -o /dev/null -m 0.2 "http://127.0.0.1:${PORT}/" 2>/dev/null && break; sleep 0.1; done
56
+ mkdir -p "$WORK/home/.crewly" "$WORK/proj/out"
57
+ ENVS=(CREWLY_API_URL="http://127.0.0.1:${PORT}" CREWLY_SESSION_NAME=dev-ella HOME="$WORK/home" CREWLY_HOME="$WORK/home/.crewly" CREWLY_PROJECT_PATH="$WORK/proj")
58
+ run() { env "${ENVS[@]}" bash "$EXEC" "$@" 2>/dev/null; }
59
+ run_err() { env "${ENVS[@]}" bash "$EXEC" "$@" 2>&1 >/dev/null; }
60
+
61
+ : > "$STUB_LOG" # drop the readiness probe
62
+ head -c 3000000 /dev/zero > "$WORK/proj/out/daily.mp4"
63
+ OUT=$(run --path "$WORK/proj/out/daily.mp4" --note "the HD cut")
64
+ check "result" "$(printf '%s' "$OUT" | jq -c '{success, shareId, url, fileName, sizeBytes, expiresAt}')" '{"success":true,"shareId":"sf_AbCdEfGhIjKlMnOpQrStUv","url":"https://crewlyai.com/portal/files/sf_AbCdEfGhIjKlMnOpQrStUv","fileName":"daily.mp4","sizeBytes":3000000,"expiresAt":"2026-10-20T00:00:00.000Z"}'
65
+ check "instruction tells the agent to post the url in its own words" "$(printf '%s' "$OUT" | jq -r '.instruction' | grep -c 'in your own words')" "1"
66
+ check "instruction names cancel" "$(printf '%s' "$OUT" | jq -r '.instruction' | grep -c 'share-file/execute.sh --cancel sf_AbCdEfGhIjKlMnOpQrStUv')" "1"
67
+ check "output never carries the signed url" "$(printf '%s' "$OUT" | grep -c 'SECRETSIG')" "0"
68
+ check "create request" "$(sed -n 1p "$STUB_LOG" | jq -c '{req, session}')" '{"req":{"fileName":"daily.mp4","sizeBytes":3000000,"contentType":"video/mp4","note":"the HD cut"},"session":"dev-ella"}'
69
+ check "upload streamed with signed headers" "$(sed -n 2p "$STUB_LOG" | jq -c '{bytes, ctype, sse}')" '{"bytes":3000000,"ctype":"video/mp4","sse":"AES256"}'
70
+ check "complete called" "$(sed -n 3p "$STUB_LOG" | jq -r .op)" "complete"
71
+
72
+ # --name overrides the file name; --content-type overrides the guess
73
+ : > "$STUB_LOG"
74
+ run --path "$WORK/proj/out/daily.mp4" --name "launch.mov" --content-type video/quicktime >/dev/null
75
+ check "name and content type" "$(sed -n 1p "$STUB_LOG" | jq -c '.req | {fileName, contentType}')" '{"fileName":"launch.mov","contentType":"video/quicktime"}'
76
+
77
+ # Big files report progress on stderr (threshold lowered for the test), and never leak the signed url
78
+ : > "$STUB_LOG"
79
+ ERR=$(env "${ENVS[@]}" SHARE_FILE_PROGRESS_MIN_BYTES=1 bash "$EXEC" --path "$WORK/proj/out/daily.mp4" 2>&1 >/dev/null)
80
+ check "progress announced" "$(printf '%s' "$ERR" | grep -c 'uploading 2 MB')" "1"
81
+ check "progress reaches 100%" "$(printf '%s' "$ERR" | grep -c 'uploaded 100%')" "1"
82
+ check "progress never prints the signed url" "$(printf '%s' "$ERR" | grep -c 'SECRETSIG')" "0"
83
+ check "progress run still uploads the whole file" "$(grep '"op": "put"' "$STUB_LOG" | jq -r .bytes)" "3000000"
84
+
85
+ : > "$STUB_LOG"
86
+ OUT=$(run --path "$WORK/proj/out/daily.mp4" --name "refuse.bin"; true)
87
+ check "cloud refusal passed through" "$(printf '%s' "$OUT" | jq -c '{success, reason}')" '{"success":false,"reason":"too_large"}'
88
+
89
+ : > "$STUB_LOG"; touch "$STORE.fail"
90
+ OUT=$(run --path "$WORK/proj/out/daily.mp4"; true)
91
+ check "failed upload reports and cancels" "$(printf '%s' "$OUT" | jq -r .reason)" "upload_failed"
92
+ check "cancel issued" "$(grep -c '"op": "cancel"' "$STUB_LOG")" "1"
93
+ rm -f "$STORE.fail"
94
+
95
+ # Path safety (same as send-file)
96
+ echo secret > "$WORK/outside.txt"; ln -s "$WORK/proj/out/daily.mp4" "$WORK/proj/link.mp4"
97
+ echo hidden > "$WORK/home/.crewly/token.txt"
98
+ mkdir -p "$WORK/proj/dir"; : > "$WORK/proj/empty.mp4"
99
+ check "outside project refused" "$(run_err --path "$WORK/outside.txt" | grep -c 'outside your project directory')" "1"
100
+ check "symlink refused" "$(run_err --path "$WORK/proj/link.mp4" | grep -c 'symbolic link')" "1"
101
+ check "directory refused" "$(run_err --path "$WORK/proj/dir" | grep -c 'not a regular file')" "1"
102
+ check "empty file refused" "$(run_err --path "$WORK/proj/empty.mp4" | grep -c 'is empty')" "1"
103
+ check "missing file refused" "$(run_err --path "$WORK/proj/nope" | grep -c 'file not found')" "1"
104
+ check "path required" "$(run_err | grep -c -- '--path is required')" "1"
105
+ : > "$STUB_LOG"
106
+ env "${ENVS[@]}" CREWLY_PROJECT_PATH="$WORK" bash "$EXEC" --path "$WORK/home/.crewly/token.txt" >/dev/null 2>&1
107
+ check "Crewly home refused even when it is inside the project" "$(grep -c '"op": "create"' "$STUB_LOG")" "0"
108
+
109
+ # --status and --cancel
110
+ : > "$STUB_LOG"
111
+ OUT=$(run --status)
112
+ check "status lists shares with links" "$(printf '%s' "$OUT" | jq -c '{success, shares}')" '{"success":true,"shares":[{"shareId":"sf_AbCdEfGhIjKlMnOpQrStUv","status":"ready","fileName":"daily.mp4","sizeBytes":3000000,"url":"https://crewlyai.com/portal/files/sf_AbCdEfGhIjKlMnOpQrStUv","expiresAt":"2026-10-20T00:00:00.000Z"}]}'
113
+ OUT=$(run --status sf_AbCdEfGhIjKlMnOpQrStUv)
114
+ check "status of one share drops signed urls" "$(printf '%s' "$OUT" | jq -c '{success, shareId, status, fileName}')" '{"success":true,"shareId":"sf_AbCdEfGhIjKlMnOpQrStUv","status":"ready","fileName":"daily.mp4"}'
115
+ check "status of one share never prints a download url" "$(printf '%s' "$OUT" | grep -c 'secret.example')" "0"
116
+ : > "$STUB_LOG"
117
+ run --cancel sf_AbCdEfGhIjKlMnOpQrStUv >/dev/null
118
+ check "cancel" "$(jq -r .path "$STUB_LOG")" "/api/apps/shares/sf_AbCdEfGhIjKlMnOpQrStUv"
119
+ check "cancel needs a share id" "$(run_err --cancel hf_AbCdEfGhIjKlMnOpQrStUv | grep -c 'share id like sf_')" "1"
120
+
121
+ echo "share-file: $PASS passed, $FAIL failed"
122
+ [ "$FAIL" -eq 0 ]
@@ -38,6 +38,8 @@ export declare const requestCapabilities: (req: Request, res: Response) => Promi
38
38
  export declare const cancelCapabilityRequest: (req: Request, res: Response) => Promise<void>;
39
39
  /** POST /api/apps/:appId/make-private */
40
40
  export declare const makePrivate: (req: Request, res: Response) => Promise<void>;
41
+ /** DELETE /api/apps/:appId — delete (unpublish) an app: the owner, or an agent whose team published it and it is private */
42
+ export declare const deleteApp: (req: Request, res: Response) => Promise<void>;
41
43
  /** POST /api/apps/:appId/transfer `{ toSession }` — hand the app to another agent / team */
42
44
  export declare const transferApp: (req: Request, res: Response) => Promise<void>;
43
45
  /** GET /api/apps/:appId/owner — who the app's comments go to (an agent, a team or a channel) */
@@ -146,4 +148,14 @@ export declare const createUploadRequest: (req: Request, res: Response) => Promi
146
148
  export declare const getUploadRequest: (req: Request, res: Response) => Promise<void>;
147
149
  /** DELETE /api/apps/upload-requests/:requestId — cancel the link */
148
150
  export declare const cancelUploadRequest: (req: Request, res: Response) => Promise<void>;
151
+ /** POST /api/apps/shares `{ fileName, sizeBytes, contentType?, note? }` — reserve a share; answers the owner's link and a presigned upload URL */
152
+ export declare const createShare: (req: Request, res: Response) => Promise<void>;
153
+ /** POST /api/apps/shares/:shareId/complete — the upload finished; Cloud checks the size and starts the 7 days */
154
+ export declare const completeShare: (req: Request, res: Response) => Promise<void>;
155
+ /** GET /api/apps/shares/:shareId — metadata (signed URLs only for the owner) */
156
+ export declare const getShare: (req: Request, res: Response) => Promise<void>;
157
+ /** GET /api/apps/shares — the account's live shares, with their links */
158
+ export declare const listShares: (req: Request, res: Response) => Promise<void>;
159
+ /** DELETE /api/apps/shares/:shareId — withdraw; Cloud deletes the file */
160
+ export declare const cancelShare: (req: Request, res: Response) => Promise<void>;
149
161
  //# sourceMappingURL=apps.controller.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"apps.controller.d.ts","sourceRoot":"","sources":["../../../../../../backend/src/controllers/apps/apps.controller.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"apps.controller.d.ts","sourceRoot":"","sources":["../../../../../../backend/src/controllers/apps/apps.controller.ts"],"names":[],"mappings":"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"}