@entrydesk/cli 2.0.0 → 2.1.0-beta.2

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 (81) hide show
  1. package/README.md +296 -1
  2. package/dist/add-DXYghmJz.js +2 -0
  3. package/dist/agents-Drg2ZD85.js +2 -0
  4. package/dist/billing-Bkk1BhAw.js +2 -0
  5. package/dist/{budget-DcULs51p.js → budget-dazpYOON.js} +1 -1
  6. package/dist/build-task-request-config-BDlX-M_P.js +2 -0
  7. package/dist/commands-DUKXJUfr.js +2 -0
  8. package/dist/commands-FVFeS1MV.js +2 -0
  9. package/dist/{config-CI3k8CUK.js → config-CSJx1Xlt.js} +2 -2
  10. package/dist/connectors-CHcopt49.js +3 -0
  11. package/dist/file-upload-BmP4AJY0.js +16 -0
  12. package/dist/format-C3tQC6LL.js +2 -0
  13. package/dist/index.js +84 -10
  14. package/dist/instruction-BXKEUplM.js +2 -0
  15. package/dist/{login-y8SlHgZ4.js → login-ACaFCfsh.js} +1 -1
  16. package/dist/{logout-CGGEe0it.js → logout-BX4YsOqw.js} +1 -1
  17. package/dist/{marketplace-CaIuJP0T.js → marketplace-S_4fkbWt.js} +1 -1
  18. package/dist/{mcp-Yz1qXxIb.js → mcp-DOUHU2_Z.js} +2 -2
  19. package/dist/mcp-client-LcmqajIh.js +2 -0
  20. package/dist/meeting-BG76tnT9.js +3 -0
  21. package/dist/models-CkepW_-c.js +3 -0
  22. package/dist/notifications-BAMcYXhl.js +2 -0
  23. package/dist/options-CgmngSTC.js +2 -0
  24. package/dist/paginate-DUu8qbek.js +2 -0
  25. package/dist/paths-DQh1PdjS.js +2 -0
  26. package/dist/{profile-1uJ5EHdo.js → profile-BzXbLYDw.js} +1 -1
  27. package/dist/profile-storage-DlaupNlV.js +60 -0
  28. package/dist/project-B9uLW5nn.js +2 -0
  29. package/dist/project-context-VlX5GZxr.js +2 -0
  30. package/dist/project-governance-CwVlxGE2.js +2 -0
  31. package/dist/project-sources-BzMkopCj.js +2 -0
  32. package/dist/project-sources-write-D3WzWqet.js +2 -0
  33. package/dist/project-tasks-CnXLm061.js +2 -0
  34. package/dist/push-DWIgzvqe.js +2 -0
  35. package/dist/{recordings-C0Ij3tfP.js → recordings-_HnTtukO.js} +1 -1
  36. package/dist/remove-Cpen9pWn.js +2 -0
  37. package/dist/resolve-7OXPmNrX.js +6 -0
  38. package/dist/resource-grants-D-ImlByb.js +2 -0
  39. package/dist/schedules-DACWrdY_.js +21 -0
  40. package/dist/skills-BenhDHKq.js +2 -0
  41. package/dist/status-BKd81bGK.js +2 -0
  42. package/dist/{status-D6ebFbsw.js → status-ErhWpQxW.js} +1 -1
  43. package/dist/stdin-DzzLUF6W.js +2 -0
  44. package/dist/task-CvQD8Y4d.js +104 -0
  45. package/dist/tasks-C4dAgHmR.js +2 -0
  46. package/dist/tasks-CnncSIan.js +14 -0
  47. package/dist/terminal-text-D04u1hg1.js +2 -0
  48. package/dist/{tool-3NEPlGNR.js → tool-CpSoLUlR.js} +1 -1
  49. package/dist/update-D09UponU.js +2 -0
  50. package/dist/usage-Dibnsgtg.js +3 -0
  51. package/dist/{usage-audit-e6kpW0LZ.js → usage-audit-Be431Kj1.js} +3 -3
  52. package/dist/version-BTqAbDeB.js +2 -0
  53. package/dist/workflow-Dx41WM43.js +6 -0
  54. package/dist/{workspaces-BQiFZ5pV.js → workspaces-rVOwf5zp.js} +1 -1
  55. package/package.json +8 -4
  56. package/dist/add-CDlJBikf.js +0 -2
  57. package/dist/agents-vivPBsWo.js +0 -2
  58. package/dist/billing-BietwbvI.js +0 -2
  59. package/dist/build-task-request-config-Bbffa_rv.js +0 -2
  60. package/dist/commands-BCLJEd6x.js +0 -2
  61. package/dist/commands-CpuinJar.js +0 -2
  62. package/dist/connectors-CbkKDKQQ.js +0 -3
  63. package/dist/custom-connector-CxGNx65S.js +0 -2
  64. package/dist/format-BNEUX5Xv.js +0 -2
  65. package/dist/mcp-client-DdryEToX.js +0 -2
  66. package/dist/meeting-CWv3iUU6.js +0 -3
  67. package/dist/models-DFEIoTPH.js +0 -2
  68. package/dist/notifications-CBLIBZtc.js +0 -2
  69. package/dist/paths-Cqjaa_lJ.js +0 -2
  70. package/dist/profile-storage-DYUIMuP0.js +0 -72
  71. package/dist/push-BBY3QXxO.js +0 -2
  72. package/dist/remove-D-UQJ0IM.js +0 -2
  73. package/dist/schedules-CFjtG3p4.js +0 -21
  74. package/dist/skills-16msznfK.js +0 -2
  75. package/dist/status-kRSNf0Ap.js +0 -2
  76. package/dist/task-DKK82ixE.js +0 -115
  77. package/dist/tasks-DX7CQir8.js +0 -14
  78. package/dist/update-DhNwkiwD.js +0 -2
  79. package/dist/usage-050zvE6Q.js +0 -3
  80. package/dist/version-orM51hu_.js +0 -2
  81. package/dist/workflow-DHTrGTRn.js +0 -6
package/README.md CHANGED
@@ -82,6 +82,298 @@ entrydesk tasks
82
82
  entrydesk tasks 1
83
83
  ```
84
84
 
85
+ ## Project
86
+
87
+ Projects group **sources** (the files and links you put in one), the **tasks**
88
+ that belong to it, and the **assets** those tasks produce. Its **instruction**
89
+ is the standing brief every task in the project starts from.
90
+
91
+ ```bash
92
+ entrydesk project # List projects you belong to
93
+ entrydesk project list --filter mine # all | mine | shared
94
+ entrydesk project get 1 # …by its number in that list
95
+ entrydesk project get <projectId> # …or by id
96
+ entrydesk project instruction 1 # Plain text, ready to pipe
97
+ entrydesk project edit 1 --name "Q4 Launch" --description "Launch coordination"
98
+ ```
99
+
100
+ A `<project>` number always means a position in a **plain** `project list`. A
101
+ `--filter`, `--sort-by`, `--order` or `--all` listing renumbers from 1, so those
102
+ print no position column — pass an id there.
103
+
104
+ A plain listing is in creation order, oldest first, and the number is resolved
105
+ against that listing as it stands at that moment. A project created after you
106
+ looked takes the next number, so the ones you saw keep theirs; only a deletion
107
+ shifts the numbers after it. `entrydesk tasks <n>` numbers newest first, so a
108
+ new task does shift its positions. Numbers are for typing by hand; scripts, and
109
+ anything that has to keep pointing at one project across deletions, should use
110
+ the id.
111
+
112
+ `--sort-by lastActivityAt --order desc` shows the most recently active project first, the
113
+ order the Hub uses; that listing prints ids instead of positions.
114
+
115
+ The instruction is plain text on stdout with nothing around it, so it pipes and
116
+ redirects cleanly, and a project without one prints nothing and exits 0:
117
+
118
+ ```bash
119
+ entrydesk project instruction 1 > brief.md
120
+ cat brief.md | entrydesk project edit 1 --instruction-file -
121
+ entrydesk project edit 1 --clear-instruction
122
+ ```
123
+
124
+ `--instruction`, `--instruction-file` and `--clear-instruction` are mutually
125
+ exclusive. `--instruction-file -` reads stdin, and stores exactly what it is
126
+ given — leading and trailing whitespace included, the same as a file path — so
127
+ the round-trip above returns the instruction unchanged.
128
+
129
+ Project text that another member wrote (names, descriptions, the instruction as
130
+ `project get` renders it) has terminal control characters stripped before it is
131
+ displayed. `project instruction` writes the raw bytes when its output is piped
132
+ or redirected, and filters them only when it is writing to your terminal.
133
+
134
+ These commands read and edit the project itself; none of them talks to the AI.
135
+
136
+ To work with the AI inside a project, pass `--project` to the task commands.
137
+ `task --project` creates its task in the project, so it starts from the project
138
+ instruction and the AI answers as it always does; `entrydesk project note` is
139
+ the other path, filing a work note with no AI reply. `tasks --project` lists or
140
+ searches only the tasks you can see in that project:
141
+
142
+ ```bash
143
+ entrydesk task --project 2 -m "List the open questions in the spec"
144
+ entrydesk task --project 2 -i # Interactive, same project
145
+ entrydesk tasks --project 2 # Tasks you can see in project 2
146
+ entrydesk tasks --project 2 -q "deploy" # …searched within it
147
+ ```
148
+
149
+ `--project` also decides what the numbers mean. `entrydesk tasks` and a bare
150
+ `-c <n>` never list a project's tasks, so inside a project both count down
151
+ `entrydesk tasks --project <project>` instead:
152
+
153
+ ```bash
154
+ entrydesk tasks --project 2 # 1 is the newest task in project 2
155
+ entrydesk task --project 2 -c 1 -m "Follow up"
156
+ ```
157
+
158
+ The same holds for `/tasks` in an interactive `--project` session: it offers
159
+ that project's tasks only, so picking one cannot move the session out of it.
160
+
161
+ A piped `task` gets both halves of what you gave it: `-m` is your instruction
162
+ and stdin is the material to work on, so this reaches the AI as one turn
163
+ carrying the file and the request.
164
+
165
+ ```bash
166
+ entrydesk project context-read 2 file <id> \
167
+ | entrydesk task --project 2 -m "Turn this into a checklist"
168
+ ```
169
+
170
+ stdin here is content from elsewhere, so it is handed over as material to read
171
+ rather than as instructions to follow, and anything inside it that looks like a
172
+ command is not one. Piped stdin on its own is still the message itself, exactly
173
+ as before, and `-i` never reads stdin.
174
+
175
+ A piped stdin is always read to its end, with or without `-m`, so a producer
176
+ that takes seconds to answer is waited for instead of being dropped:
177
+
178
+ ```bash
179
+ curl -s https://example.com/spec.md | entrydesk task --project 2 -m "列出待辦"
180
+ ```
181
+
182
+ The cost is the other side of the same rule: a command spawned with an stdin it
183
+ inherited but never sends on waits for that pipe to close. Close it explicitly
184
+ when you do not mean to send anything — `entrydesk task -m "…" </dev/null`.
185
+
186
+ ### Work notes
187
+
188
+ A work note is the other direction: writing down what you did without asking
189
+ the AI anything. `project note` creates a task under your name in the project,
190
+ private until you share it, and no AI reply is triggered. The content comes
191
+ from `-m`, then `-f`, then stdin — the first one given wins, and all three are
192
+ equivalent:
193
+
194
+ ```bash
195
+ entrydesk project note 2 -m "Shipped the retry fix"
196
+ entrydesk project note 2 -f ./summary.md
197
+ git log -1 | entrydesk project note 2 --title "Fixed the login race"
198
+ ```
199
+
200
+ It prints the new task's id and a Hub link; `--json` returns
201
+ `{ "taskId", "projectId", "url" }`. Reach for `task --project` above whenever
202
+ you want the AI to answer instead.
203
+
204
+ ### Tasks in a project
205
+
206
+ `project tasks` is the project's own listing — the same tasks `tasks --project`
207
+ shows, printed with their ids so they can be handed to the commands below. The
208
+ other three do what the Hub's task menu does: move a task of yours in or out,
209
+ and share it inside the project. Only the task's owner can do any of this; the
210
+ API refuses everyone else with `Task not found`, the same answer as for a task
211
+ that does not exist.
212
+
213
+ ```bash
214
+ entrydesk project tasks 1 # Tasks you can see in project 1
215
+ entrydesk project tasks 1 -q "deploy" # …searched within it
216
+ entrydesk project task-add 1 <taskId> # Move your task in
217
+ entrydesk project task-add 1 <taskId> --confirm-asset-sharing
218
+ entrydesk project task-remove 1 <taskId> # Move it out; its assets stay
219
+ entrydesk project task-share 1 <taskId> --add-user <userId>
220
+ entrydesk project task-share 1 <taskId> --remove-user <userId>
221
+ entrydesk project task-share 1 <taskId> --visibility project
222
+ entrydesk project task-share 1 <taskId> --visibility self
223
+ ```
224
+
225
+ Moving a task in also files the assets it produced with the project, where every
226
+ member can view and use them. When the task carries assets the project does not
227
+ hold yet, `task-add` stops and says so instead of sharing them on its own —
228
+ re-run it with `--confirm-asset-sharing` to go ahead. Under `--json` that
229
+ refusal is the API's own `asset_sharing_confirmation_required` code on stderr.
230
+ Moving a task out leaves its assets in the project along with their version
231
+ history; only the task moves.
232
+
233
+ `task-share` does one thing per call and never prompts. `--add-user` and
234
+ `--remove-user` name one project member by id (`project get <project> --json`
235
+ lists the members with theirs); `--visibility project` opens the task to every
236
+ member and `--visibility self` closes it again, keeping whoever you named. The
237
+ three flags are mutually exclusive and each needs its value; with none of them
238
+ the command prints the usage and exits 1. A task has to be in the project you
239
+ name — `task-share` and `task-remove` refuse one that is elsewhere rather than
240
+ acting on the project it is actually in.
241
+
242
+ `project tasks` reads with `tasks:read`; the three that write need
243
+ `tasks:write`, and `task-remove` and `task-share` also read the task first with
244
+ `tasks:read`. A project you are not a member of answers `Project not found`
245
+ from all four.
246
+
247
+ ### Sources and context
248
+
249
+ **Sources** are the files and links you put in a project — the same list the
250
+ Hub's Sources tab shows, with each one's retrieval description and, for a link,
251
+ whether its snapshot is `pending`, `ready` or `failed`. **Context** is
252
+ everything the AI knows in the project, so searching and reading across the
253
+ tasks you can see _and_ those sources hangs off that word instead:
254
+
255
+ ```bash
256
+ entrydesk project sources 1
257
+ entrydesk project context-search 1 "login race" --type task --limit 5
258
+ entrydesk project context-read 1 file <fileId> # A file's stored text
259
+ entrydesk project context-read 1 task <taskId> # A task's summary
260
+ entrydesk project context-read 1 task <taskId> --full # …its whole history
261
+ ```
262
+
263
+ `context-search` looks across tasks and sources at once; `--type task`, `--type
264
+ file` or `--type link` narrows it to one kind. Assets are not on this surface —
265
+ view them in the Hub — and asking for one fails the same way every other
266
+ project command fails, JSON on stderr included.
267
+
268
+ `sources` lists what the API serves in one shot, which is the newest 200 files
269
+ and the newest 200 links. A project holding more says so on stderr, and under
270
+ `--json` the payload carries `truncated: true` beside the rows.
271
+
272
+ The `sources-*` commands maintain that list the way the Hub's Sources tab does
273
+ — upload a file, add a link, edit a description, re-fetch a link, remove one.
274
+ `sources-add` takes a local file path, or a link when the argument starts with
275
+ `http://` or `https://`; the file is uploaded and then attached to the project.
276
+ `<file|link>` and `<id>` are exactly what `project sources` prints for a row:
277
+
278
+ ```bash
279
+ entrydesk project sources-add 1 ./brief.pdf --description "The launch brief"
280
+ entrydesk project sources-add 1 https://example.com/spec
281
+ entrydesk project sources-edit 1 file <fileId> --description "Signed-off scope"
282
+ entrydesk project sources-refetch 1 <linkId> # Snapshot goes back to pending
283
+ entrydesk project sources-rm 1 link <linkId>
284
+ ```
285
+
286
+ A link starts fetching its snapshot as soon as it is added, and `sources-refetch`
287
+ starts that over, so both report it as `pending`; `project sources` shows when
288
+ it is `ready`. Re-fetching a link whose fetch is still in flight is refused
289
+ rather than queued twice. Assets are removed in the Hub, so `sources-rm` takes
290
+ only `file` or `link`. `--json` prints the source the API returned with a
291
+ `type` field beside it, and `sources-rm --json` prints `{ "type", "id",
292
+ "removed": true }`.
293
+
294
+ Uploading a file, attaching it and saving its `--description` is one request,
295
+ declared on `projects:write` like every other `sources-*` call; nothing is
296
+ left behind when the project refuses it. A token short of that scope is told so.
297
+
298
+ `context-read` writes the source's text and nothing else to stdout, so it pipes
299
+ straight into a task in the same project:
300
+
301
+ ```bash
302
+ entrydesk project context-read 1 file <fileId> \
303
+ | entrydesk task --project 1 -m "List the work this implies"
304
+ ```
305
+
306
+ Everything that is not text stays off stdout, so a pipe never carries an
307
+ explanation where the content should be: a file with no text to extract, a link
308
+ whose snapshot is still being fetched, and one whose fetch failed each print
309
+ their reason on stderr and exit non-zero. With `--json` that reason is the
310
+ `{ "status": … }` object from the API — `download` with a short-lived
311
+ `downloadUrl`, `pending`, or `failed` with the error — again on stderr.
312
+
313
+ A read that is cut short says so on stderr too, never on stdout, and names the
314
+ `--offset` to resume from where the source can be paged. With `--json` the same
315
+ facts are the `truncated` and `nextOffset` fields of the object on stdout.
316
+ `--offset` implies `--full`, since a summary is not paged and an offset against
317
+ one means nothing — resuming needs only the offset the previous read reported.
318
+
319
+ Both depths are a task's: a file or a link snapshot has one, and the server
320
+ serves its stored text up to a fixed cap whether or not you pass `--full`. That
321
+ is why a truncated one reports no offset to resume from — it says it was cut
322
+ short and there is nothing further to ask for.
323
+
324
+ ### Governance
325
+
326
+ A project can be created, deleted and staffed without opening the Hub, under
327
+ the same rules the Hub applies. Whoever creates it is its owner:
328
+
329
+ ```bash
330
+ entrydesk project create --name "Q4 Launch" --description "Launch coordination"
331
+ cat brief.md | entrydesk project create --name "Q4 Launch" --instruction-file -
332
+ entrydesk project members 1 # Each member, role and id
333
+ entrydesk project member-add 1 <userId> # Takes effect at once
334
+ entrydesk project member-role 1 <userId> --role owner
335
+ entrydesk project member-remove 1 <userId> # Your own id leaves the project
336
+ entrydesk project delete 1 # Lists what goes, then asks
337
+ entrydesk project delete 1 -y # No prompt
338
+ ```
339
+
340
+ `create` prints the new id and its Hub address, and with `--json` the payload
341
+ carries both. `--instruction` and `--instruction-file` are the same options
342
+ `edit` takes, with the same limit.
343
+
344
+ Only an owner can delete a project, add or remove someone else, or change a
345
+ role; a member can leave. A user id is the UUID `project members` prints beside
346
+ each member — a workspace member who is not in the project yet is found in the
347
+ Hub's member picker. The last owner cannot leave or step down to member: promote
348
+ someone else first. Each refusal comes back as its own message, and with
349
+ `--json` under its own code (`project_owner_required`,
350
+ `project_last_owner_required`, `project_member_already_exists`,
351
+ `project_invitee_not_in_workspace`, `project_member_not_found`). A project you
352
+ are not in is `Project not found` for every one of these commands, exactly as
353
+ it is for `get`.
354
+
355
+ Deleting a project detaches its tasks, which return to their owners' main
356
+ lists, and deletes its sources, its assets and every version in their history.
357
+ Without `-y` the command prints those effects for the project in question and
358
+ waits for a `y`; with no terminal to ask on it refuses instead (exit code 1,
359
+ JSON on stderr under `--json`), so a stray id in a script cannot delete
360
+ anything.
361
+
362
+ A project command fails in one of three ways, each with its own message: the
363
+ server has projects turned off, your token is missing a scope the command
364
+ needs (the message names each missing scope — run `entrydesk login` again, or
365
+ issue a PAT that grants it), or the project is not one you can see — reported
366
+ as `Project not found` whether it is someone else's or does not exist. With
367
+ `--json` the error is JSON on stderr and the exit code is non-zero.
368
+
369
+ Commands that only read or edit the project itself need `projects:read` or
370
+ `projects:write`; the task commands need `tasks:read` or `tasks:write`.
371
+ `project note`, `context-search` and `context-read` reach both a project's
372
+ sources and its tasks, so each requires both families at once: `project note`
373
+ needs `projects:write` and `tasks:write`, and the two context commands need
374
+ `projects:read` and `tasks:read`. `entrydesk login` grants all four; a PAT has
375
+ to be issued with them.
376
+
85
377
  ## Tools
86
378
 
87
379
  ```bash
@@ -297,7 +589,10 @@ entrydesk tasks --json
297
589
  ```
298
590
 
299
591
  Note that billing endpoints are not readable with a PAT; they require an
300
- interactive login.
592
+ interactive login. A PAT for the project commands needs `projects:read`,
593
+ `projects:write`, `tasks:read` and `tasks:write` — `project note`,
594
+ `context-search` and `context-read` each require a `projects:*` and a `tasks:*`
595
+ scope together, and refuse with the missing ones named otherwise.
301
596
 
302
597
  ## Profiles
303
598
 
@@ -0,0 +1,2 @@
1
+ #!/usr/bin/env node
2
+ import{E as e}from"./profile-storage-DlaupNlV.js";import{a as t,d as n,f as r,g as i,i as a,r as o}from"./paths-DQh1PdjS.js";import{t as s}from"./hash-Ddmw8YE4.js";import{r as c}from"./installer-iaoPJQn3.js";import{t as l}from"./remote-nEFOyIM2.js";import u from"node:fs";import d from"node:os";import f from"node:path";import p from"cross-spawn";function m(e,t){let n=f.dirname(e);u.mkdirSync(n,{recursive:!0});let r=u.mkdtempSync(f.join(n,`.${f.basename(e)}.tmp-`)),i=f.join(r,`next`),a=f.join(r,`prev`);u.mkdirSync(i,{recursive:!0});let o=!1;try{t(i),u.existsSync(e)&&(u.renameSync(e,a),o=!0),u.renameSync(i,e)}catch(t){throw o&&!u.existsSync(e)&&u.renameSync(a,e),t}finally{u.rmSync(r,{recursive:!0,force:!0})}}const h=/^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/iu,g=/^([\w.-]+)\/([\w.-]+)(?:\/(.+))?$/u;function _(e){return e===`~`?d.homedir():e.startsWith(`~/`)||e.startsWith(`~\\`)?f.join(d.homedir(),e.slice(2)):e}function v(e){return e.startsWith(`./`)||e.startsWith(`../`)||e.startsWith(`/`)||e.startsWith(`~`)||e.startsWith(`.\\`)||/^[a-zA-Z]:[\\/]/u.test(e)}function y(e){let t=/^https?:\/\/github\.com\/([\w.-]+)\/([\w.-]+?)(?:\.git)?(?:\/tree\/([^/]+)(?:\/(.+))?)?\/?$/u.exec(e);return t?{type:`github`,owner:t[1],repo:t[2],gitRef:t[3],subpath:t[4],url:`https://github.com/${t[1]}/${t[2]}`}:null}function b(e){let t=e.trim();if(v(t))return{type:`local`,path:f.resolve(_(t))};if(t.startsWith(`ed:`)){let e=t.slice(3);return{type:`entrydesk`,ref:e,isId:h.test(e)}}if(h.test(t))return{type:`entrydesk`,ref:t,isId:!0};if(t.startsWith(`git@`)||t.endsWith(`.git`))return{type:`git`,url:t};let n=y(t);if(n)return n;if(/^https?:\/\//u.test(t))return{type:`git`,url:t};let r=g.exec(t);return r?{type:`github`,owner:r[1],repo:r[2],subpath:r[3],url:`https://github.com/${r[1]}/${r[2]}`}:{type:`entrydesk`,ref:t,isId:!1}}function x(e){return e.type===`github`?`github`:e.type}const S=new Set([`.git`,`node_modules`,`__pycache__`,`.DS_Store`,`.entrydesk`]);function C(e,t){if(!u.existsSync(f.join(e,`SKILL.md`)))throw Error(`No SKILL.md found at ${e}`);let n=u.realpathSync(e);if(n===(u.existsSync(t)?u.realpathSync(t):f.resolve(t)))return;let r=u.mkdtempSync(f.join(d.tmpdir(),`ed-local-skill-`)),i=f.join(r,`skill`);try{u.cpSync(n,i,{recursive:!0,filter:e=>!S.has(f.basename(e))}),m(t,e=>{u.cpSync(i,e,{recursive:!0})})}finally{u.rmSync(r,{recursive:!0,force:!0})}}function w(e){try{let t=u.readFileSync(f.join(e,`SKILL.md`),`utf8`),n=/^name:\s*(.+?)\s*$/mu.exec(t.split(/^---\s*$/mu)[1]??``);if(n?.[1])return n[1].replace(/^['"]|['"]$/gu,``)}catch{}return f.basename(e)}async function T(e,t){let{default:n}=await import(`adm-zip`),r=new n(e);m(t,e=>{let t=f.resolve(e)+f.sep;for(let n of r.getEntries()){if(n.isDirectory)continue;let r=f.resolve(e,n.entryName);if(!r.startsWith(t))throw Error(`Zip entry contains path traversal: ${n.entryName}`);u.mkdirSync(f.dirname(r),{recursive:!0}),u.writeFileSync(r,n.getData())}})}async function E(e,t){if(t.isId)return t.ref;let n=(await l(e.client,e.accessToken,e.workspaceId)).find(e=>e.name===t.ref||e.id===t.ref);if(!n)throw Error(`Skill not found in workspace: ${t.ref}`);return n.id}function D(e,t,n){let r=u.mkdtempSync(f.join(d.tmpdir(),`ed-npx-skills-`)),i=[`-y`,`skills@1.5.12`,`add`,e,`--copy`,`--yes`];try{let e=p.sync(`npx`,i,{cwd:r,stdio:n?[`ignore`,`pipe`,`pipe`]:`inherit`});if(e.error)throw e.error;if(e.status!==0){let t=e.stderr?.toString().trim();throw Error(`npx exited with code ${e.status??`null`}${t?`: ${t}`:``}`)}}catch(t){throw u.rmSync(r,{recursive:!0,force:!0}),Error(`npx skills failed for ${e}. Ensure npx + network are available. (${t instanceof Error?t.message:String(t)})`,{cause:t})}let a=f.join(r,`.agents`,`skills`);if(!u.existsSync(a))throw u.rmSync(r,{recursive:!0,force:!0}),Error(`npx skills produced no skills under ${a}`);let o=u.readdirSync(a,{withFileTypes:!0}).filter(e=>e.isDirectory());if(o.length===0)throw u.rmSync(r,{recursive:!0,force:!0}),Error(`npx skills installed no skill directory`);let s=o[0];if(o.length>1){let e=t.type===`github`&&t.subpath?f.basename(t.subpath):void 0,n=e?o.find(t=>t.name===e):void 0;if(!n)throw u.rmSync(r,{recursive:!0,force:!0}),Error(`npx skills installed [${o.map(e=>e.name).join(`, `)}]; ${e?`none matched the subpath "${e}"`:`specify a subpath to pick one`}.`);s=n}return{dir:f.join(a,s.name),tmpRoot:r}}async function O(l,d,f){let p=b(l),m=!!d.global,h=a(m,f.workspaceId),g=h.rootDir,_={...f,workspaceId:h.workspaceId};if(!h.linked){let i={workspaceId:f.workspaceId,apiUrl:e()};t(),m?n(i):r(process.cwd(),i)}let v,y,S;if(p.type===`entrydesk`){y=await E(_,p);let e=await _.client.downloadSkillCurrent(_.accessToken,_.workspaceId,y);v=e.name,S=o(g,v);let t=await fetch(e.url);if(!t.ok)throw Error(`Failed to download ${v}: ${t.status}`);await T(Buffer.from(await t.arrayBuffer()),S)}else if(p.type===`local`)v=w(p.path),S=o(g,v),C(p.path,S);else{let{dir:e,tmpRoot:t}=D(l,p,!!d.json);try{v=w(e),S=o(g,v),C(e,S)}finally{u.rmSync(t,{recursive:!0,force:!0})}}let O=s(S),k={source:l,sourceType:x(p),skillId:y,localHash:O,lastPulledHash:p.type===`entrydesk`?O:``,mode:`symlink`,...p.type===`github`?{sourceUrl:p.url,ref:p.gitRef,subpath:p.subpath}:{},...p.type===`git`?{sourceUrl:p.url}:{}};i(g,v,k);let A=c(S,v,g,`symlink`,d.agent);d.json?console.info(JSON.stringify({name:v,sourceType:x(p),hash:O},null,2)):console.info(`✓ added ${v} (${p.type}, ${O.slice(0,12)}) → agents: ${A.map(e=>e.agentId).join(`, `)}`)}export{O as runSkillsAdd};
@@ -0,0 +1,2 @@
1
+ #!/usr/bin/env node
2
+ import{At as e,Nt as t,Pt as n,at as r,b as i,u as a}from"./profile-storage-DlaupNlV.js";import{t as o}from"./resource-grants-D-ImlByb.js";function s(t){return t?n([...new Set(t)]):e}function c(e){console.log(`Available agents (${e.length}):\n`);for(let t of e){if(console.log(` ${t.name} (${t.visibility})`),t.description){let e=t.description.length>80?`${t.description.slice(0,80)}…`:t.description;console.log(` ${e}`)}console.log(` ID: ${t.id}`),console.log(``)}}function l(e){console.log(`My agents (${e.length}):\n`);for(let t of e){let e=[];t.latestVersionNumber!=null&&e.push(`v${t.latestVersionNumber}`),t.hasUnpublishedChanges&&e.push(`unpublished changes`);let n=e.length>0?` (${e.join(` · `)})`:``;if(console.log(` ${t.name}${n}`),t.description){let e=t.description.length>80?`${t.description.slice(0,80)}…`:t.description;console.log(` ${e}`)}console.log(` ${t.myRole??`no-role`} · ID: ${t.id}`),console.log(``)}}function u(e){if(console.log(`${e.name}\n`),console.log(` ID: ${e.id}`),e.visibility&&console.log(` Visibility: ${e.visibility}`),e.model&&console.log(` Model: ${e.model.name}`),e.author&&console.log(` Author: ${e.author.name}${e.author.isMe?` (you)`:``}`),e.description&&console.log(` Description: ${e.description}`),e.capabilities){let n=t(e.capabilities).map(e=>r(e));n.length>0&&console.log(` Capabilities: ${n.join(`, `)}`)}e.connectors&&e.connectors.length>0&&console.log(` Connectors: ${e.connectors.map(e=>e.name).join(`, `)}`),e.tools&&e.tools.length>0&&console.log(` Tools: ${e.tools.length}`),e.conversationStarters&&e.conversationStarters.length>0&&console.log(` Starters: ${e.conversationStarters.length}`),e.systemPrompt&&console.log(`\n System prompt:\n${e.systemPrompt}`)}async function d(e){let{accessToken:t,workspaceId:n}=await a(),r=new i;if(e.mine){let i=[],a=e.all?void 0:e.nextKey;for(let o=0;;o++){o>=100&&(console.error(`Reached maximum page limit (100). Use --limit and --next-key for manual pagination.`),process.exit(1));let s=await r.getMyAgents(t,n,{limit:e.limit,nextKey:a});if(i.push(...s.agents),a=s.nextKey,!e.all||!a)break}if(e.json){console.log(JSON.stringify({agents:i,nextKey:a}));return}if(i.length===0){console.log("You own no agents in this workspace. Create one with `entrydesk agents create --name <name> …`, or browse `entrydesk marketplace`.");return}l(i),!e.all&&a&&console.log(`Next page: --next-key ${a}`);return}if(e.all){let i=[],a;for(let o=0;;o++){o>=100&&(console.error(`Reached maximum page limit (100). Use --limit and --next-key for manual pagination.`),process.exit(1));let s=await r.getAgents(t,n,{limit:e.limit??100,nextKey:a});if(i.push(...s.agents),a=s.nextKey,!a)break}if(e.json){console.log(JSON.stringify({agents:i}));return}if(i.length===0){console.log("No agents in this workspace yet. Create one with `entrydesk agents create`, or browse `entrydesk marketplace`.");return}c(i);return}let{agents:o,nextKey:s}=await r.getAgents(t,n,{limit:e.limit,nextKey:e.nextKey});if(e.json){console.log(JSON.stringify({agents:o,nextKey:s}));return}if(o.length===0){console.log("No agents in this workspace yet. Create one with `entrydesk agents create`, or browse `entrydesk marketplace`.");return}c(o),s&&console.log(`Next page: --next-key ${s}`)}async function f(e,t){let{accessToken:n,workspaceId:r}=await a(),o=await new i().getAgent(n,r,e);if(t.json){console.log(JSON.stringify(o));return}u(o)}async function p(e){let{accessToken:t,workspaceId:n}=await a(),r=await new i().createAgent(t,n,{name:e.name,description:e.description,systemPrompt:e.systemPrompt,modelId:e.modelId,connectorIds:e.connectorIds??[],capabilities:s(e.capabilities),conversationStarters:e.conversationStarters??[],tools:e.tools,iconId:e.iconId});if(e.json){console.log(JSON.stringify(r));return}console.log(`Created agent: ${r.name}`),console.log(` ID: ${r.id}`)}async function m(e,t){let{accessToken:n,workspaceId:r}=await a(),o=new i,c=await o.getAgent(n,r,e),l={name:t.name??c.name,description:t.description??c.description,systemPrompt:t.systemPrompt??c.systemPrompt??``,modelId:t.modelId??c.model?.id??``,connectorIds:t.connectorIds??c.connectors?.map(e=>e.id)??[],capabilities:t.capabilities?s(t.capabilities):c.capabilities??{},conversationStarters:t.conversationStarters??c.conversationStarters??[],tools:t.tools??c.tools??[],iconId:t.iconId??c.iconId};await o.updateAgent(n,r,e,l),console.log(`Updated agent: ${e}`)}async function h(e){let{accessToken:t,workspaceId:n}=await a();await new i().deleteAgent(t,n,e),console.log(`Deleted agent: ${e}`)}async function g(e,t){let{accessToken:n,workspaceId:r}=await a();if(await new i().publishAgent(n,r,e),t.json){console.log(JSON.stringify({id:e,published:!0}));return}console.log(`Published agent to the workspace: ${e}`)}async function _(e,t){let{accessToken:n,workspaceId:r}=await a(),s=new i;if(t.addUser){let i=await s.upsertAgentGrant(n,r,e,t.addUser,t.role);if(t.json){console.log(JSON.stringify(i));return}console.log(`Granted ${t.role} to user ${t.addUser}`);return}if(t.removeUser){if(await s.removeAgentGrant(n,r,e,t.removeUser),t.json){console.log(JSON.stringify({userId:t.removeUser,removed:!0}));return}console.log(`Revoked access for user ${t.removeUser}`);return}let c=await s.getAgentGrants(n,r,e);if(t.json){console.log(JSON.stringify(c));return}o(c)}export{p as createAgent,h as deleteAgent,d as listAgents,g as publishAgent,_ as shareAgent,f as showAgent,m as updateAgent};
@@ -0,0 +1,2 @@
1
+ #!/usr/bin/env node
2
+ import{b as e,u as t,v as n}from"./profile-storage-DlaupNlV.js";import{r}from"./format-C3tQC6LL.js";import{t as i}from"./paginate-DUu8qbek.js";function a(e){if(console.log(`Billing status: ${e.lifecycle.kind}`),e.subscription){let t=e.subscription,n=t.nextInvoiceDueAt?` · next invoice ${new Date(t.nextInvoiceDueAt).toLocaleDateString()}`:``;console.log(` Plan: ${t.planName} · ~${r(t.estimatedMonthlyChargeUsd)}/month${n}`)}else console.log(` Plan: (no active subscription)`);let t=e.seats;console.log(` Seats: pro ${t.assigned.pro}/${t.purchased.pro}, premium ${t.assigned.premium}/${t.purchased.premium} (assigned/purchased)`);let n=e.extraUsage;console.log(` Balance: ${r(n.balanceUsd)} extra usage`),console.log(` Spent: ${r(n.currentSpentUsd)} this period`),n.monthlyExtraUsageLimitUsd!=null&&console.log(` Limit: ${r(n.monthlyExtraUsageLimitUsd)} / month`)}function o(e){console.log(`Invoices (${e.length}):\n`);for(let t of e){let e=new Date(t.billingDate).toLocaleDateString();console.log(` ${e} · ${t.type} · ${t.status} · ${r(t.totalUsd)}`),t.hostedInvoiceUrl&&console.log(` ${t.hostedInvoiceUrl}`),console.log(` ID: ${t.id}`),console.log(``)}}async function s(){return n()&&(console.error("Billing requires an interactive login. A workspace PAT (ENTRYDESK_TOKEN) cannot read billing — run `entrydesk login`."),process.exit(1)),t()}async function c(t){let{accessToken:n,workspaceId:r}=await s(),i=await new e().getBillingOverview(n,r);if(t.json){console.log(JSON.stringify(i));return}a(i)}async function l(t){let{accessToken:n,workspaceId:r}=await s(),a=new e;if(t.all){let e=await i(async e=>{let i=await a.getBillingInvoices(n,r,{limit:t.limit??20,nextKey:e});return{items:i.invoices,nextKey:i.nextKey}});if(t.json){console.log(JSON.stringify({invoices:e}));return}if(e.length===0){console.log(`No invoices.`);return}o(e);return}let{invoices:c,nextKey:l}=await a.getBillingInvoices(n,r,{limit:t.limit,nextKey:t.nextKey});if(t.json){console.log(JSON.stringify({invoices:c,nextKey:l}));return}if(c.length===0){console.log(`No invoices.`);return}o(c),l&&console.log(`Next page: --next-key ${l}`)}export{l as listInvoices,c as showBillingStatus};
@@ -1,2 +1,2 @@
1
1
  #!/usr/bin/env node
2
- import{b as e,l as t}from"./profile-storage-DYUIMuP0.js";async function n(n){let{accessToken:r,workspaceId:i}=await t(),a=await new e().getBudget(r,i);if(n.json){console.log(JSON.stringify(a));return}if(a.budget===null){console.log(`No budget set for this workspace.`);return}console.log(`Current workspace budget: $${a.budget}`)}async function r(n,r){let{accessToken:i,workspaceId:a}=await t(),o=new e;if(await o.updateBudget(i,a,n),r.json){console.log(JSON.stringify(await o.getBudget(i,a)));return}console.log(`Budget updated: $${n}`)}async function i(n){let{accessToken:r,workspaceId:i}=await t(),a=new e;if(await a.updateBudget(r,i,null),n.json){console.log(JSON.stringify(await a.getBudget(r,i)));return}console.log(`Budget cleared.`)}export{i as clearBudget,r as setBudget,n as showBudget};
2
+ import{b as e,u as t}from"./profile-storage-DlaupNlV.js";async function n(n){let{accessToken:r,workspaceId:i}=await t(),a=await new e().getBudget(r,i);if(n.json){console.log(JSON.stringify(a));return}if(a.budget===null){console.log(`No budget set for this workspace.`);return}console.log(`Current workspace budget: $${a.budget}`)}async function r(n,r){let{accessToken:i,workspaceId:a}=await t(),o=new e;if(await o.updateBudget(i,a,n),r.json){console.log(JSON.stringify(await o.getBudget(i,a)));return}console.log(`Budget updated: $${n}`)}async function i(n){let{accessToken:r,workspaceId:i}=await t(),a=new e;if(await a.updateBudget(r,i,null),n.json){console.log(JSON.stringify(await a.getBudget(r,i)));return}console.log(`Budget cleared.`)}export{i as clearBudget,r as setBudget,n as showBudget};
@@ -0,0 +1,2 @@
1
+ #!/usr/bin/env node
2
+ import{ct as e,it as t,rt as n,st as r}from"./profile-storage-DlaupNlV.js";function i({allowedTools:t,disallowedTools:n}){if(!(!t?.length&&!n?.length))return[...e(t?.map(e=>({toolName:e,policy:r.ALWAYS_ALLOW})),n?.map(e=>({toolName:e,policy:r.BLOCK})))].map(([e,t])=>({toolName:e,policy:t}))}function a({agentId:e,model:r,projectId:i,connectorIds:a,clientTools:o,clientToolSystemPrompt:s,toolApprovalMode:c,toolPolicies:l}){return{...i?{projectId:i}:{},...e?{agentId:e}:{...r?{model:r}:{},...a?.length?{connectorIds:a}:{}},...o?{clientTools:t(o,n.DIRECT)}:{},...s?{clientToolSystemPrompt:s}:{},...c?{toolApprovalMode:c}:{},...l?.length?{toolPolicies:l}:{}}}export{i as n,a as t};
@@ -0,0 +1,2 @@
1
+ #!/usr/bin/env node
2
+ import{E as e}from"./profile-storage-DlaupNlV.js";import{a as t,c as n,d as r,f as i,g as a,i as o,l as s,m as c,o as l,r as u,s as d,u as f}from"./paths-DQh1PdjS.js";import{t as p}from"./hash-Ddmw8YE4.js";import{r as m}from"./installer-iaoPJQn3.js";import h from"node:path";import g from"node:fs/promises";const _=/^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/iu;function v(a,o,s){let c=a&&_.test(a)?a:s.workspaceId,l={workspaceId:c,apiUrl:e()};if(t(),o.global)r(l),console.info(`Linked ${d()} → workspace ${c}`);else{let e=process.cwd();i(e,l),console.info(`Linked ${n(e)} → workspace ${c}`)}}function y(e){if(e.global){s()?console.info(`Unlinked ${d()}`):console.info(`No global link found.`);return}let t=l(process.cwd());t&&f(t)?console.info(`Unlinked ${n(t)}`):console.info(`No project link found.`)}async function b(e,t,n,r,i){let o=n.skillId;if(!o){let{skills:t}=await e.client.listAvailableSkills(e.accessToken,e.workspaceId),r=t.find(e=>e.name===n.name);if(!r)throw Error(`Skill not found in workspace: ${n.name}`);o=r.id}let{url:s,name:c}=await e.client.downloadSkillCurrent(e.accessToken,e.workspaceId,o),l=await fetch(s);if(!l.ok)throw Error(`Failed to download ${c}: ${l.status}`);let d=Buffer.from(await l.arrayBuffer()),f=u(t,c);await g.rm(f,{recursive:!0,force:!0}),await g.mkdir(f,{recursive:!0});let{default:_}=await import(`adm-zip`),v=h.resolve(f)+h.sep;for(let e of new _(d).getEntries()){if(e.isDirectory)continue;let t=h.resolve(f,e.entryName);if(!t.startsWith(v))throw Error(`Zip entry contains path traversal: ${e.entryName}`);await g.mkdir(h.dirname(t),{recursive:!0}),await g.writeFile(t,e.getData())}let y=p(f);a(t,c,{source:o,sourceType:`entrydesk`,skillId:o,localHash:y,lastPulledHash:y,mode:`symlink`});let b=m(f,c,t,`symlink`,i);return r||console.info(`✓ pulled ${c} (${y.slice(0,12)}) → agents: ${b.map(e=>e.agentId).join(`, `)}`),{name:c,hash:y}}async function x(e,t,n){let{rootDir:r,workspaceId:i}=o(!!t.global,n.workspaceId),a={...n,workspaceId:i},s=!!t.json,l=[];if(e)l.push(_.test(e)?{skillId:e}:{name:e});else{let e=c(r).skills;for(let[t,n]of Object.entries(e))l.push({skillId:n.skillId,name:t});if(l.length===0){console.info(`Nothing to pull — no tracked skills. Try: entrydesk skills pull <name>`);return}}let u=[];for(let e of l)u.push(await b(a,r,e,s,t.agent));s&&console.info(JSON.stringify(u,null,2))}export{x as n,y as r,v as t};
@@ -0,0 +1,2 @@
1
+ #!/usr/bin/env node
2
+ import{r as e}from"./commands-DUKXJUfr.js";export{e as runSkillsUnlink};
@@ -1,3 +1,3 @@
1
1
  #!/usr/bin/env node
2
- import{D as e,O as t,t as n}from"./profile-storage-DYUIMuP0.js";function r(e){try{let{pathname:t}=new URL(e);return t.replace(/\/+$/u,``).endsWith(`/api`)}catch{return!1}}function i(r){let i=t.getAll();if(r.json){console.log(JSON.stringify(i));return}let a=Object.entries(i);if(console.log(`Profile: ${n.getEffectiveProfileName()}`),a.length===0){console.log(`No configuration set.`),console.log(`Available keys: ${e.join(`, `)}`);return}console.log(`Configuration:
3
- `);for(let[e,t]of a)console.log(` ${e}=${t}`);console.log(`\nAvailable keys: ${e.join(`, `)}`)}function a(e,n){let r=t.get(e);n.json?console.log(JSON.stringify({[e]:r??null})):console.log(r||`Not set`)}function o(e,i){t.set(e,i),console.log(`Set ${e}=${i}`),console.error(`(profile: ${n.getEffectiveProfileName()})`),e===`apiUrl`&&r(i)&&console.error(`Warning: ${i} looks like a web app's "/api" browser proxy path. Such proxies usually strip the Authorization header, so CLI requests will fail with 401 missing_token — prefer the API server's own URL unless you know this proxy forwards Authorization.`)}function s(e){t.unset(e),console.log(`Unset ${e}`),console.error(`(profile: ${n.getEffectiveProfileName()})`)}export{a as getConfigValue,i as listConfig,o as setConfigValue,s as unsetConfigValue};
2
+ import{M as e,j as t,t as n}from"./profile-storage-DlaupNlV.js";function r(e){try{let{pathname:t}=new URL(e);return t.replace(/\/+$/u,``).endsWith(`/api`)}catch{return!1}}function i(r){let i=e.getAll();if(r.json){console.log(JSON.stringify(i));return}let a=Object.entries(i);if(console.log(`Profile: ${n.getEffectiveProfileName()}`),a.length===0){console.log(`No configuration set.`),console.log(`Available keys: ${t.join(`, `)}`);return}console.log(`Configuration:
3
+ `);for(let[e,t]of a)console.log(` ${e}=${t}`);console.log(`\nAvailable keys: ${t.join(`, `)}`)}function a(t,n){let r=e.get(t);n.json?console.log(JSON.stringify({[t]:r??null})):console.log(r||`Not set`)}function o(t,i){e.set(t,i),console.log(`Set ${t}=${i}`),console.error(`(profile: ${n.getEffectiveProfileName()})`),t===`apiUrl`&&r(i)&&console.error(`Warning: ${i} looks like a web app's "/api" browser proxy path. Such proxies usually strip the Authorization header, so CLI requests will fail with 401 missing_token — prefer the API server's own URL unless you know this proxy forwards Authorization.`)}function s(t){e.unset(t),console.log(`Unset ${t}`),console.error(`(profile: ${n.getEffectiveProfileName()})`)}export{a as getConfigValue,i as listConfig,o as setConfigValue,s as unsetConfigValue};
@@ -0,0 +1,3 @@
1
+ #!/usr/bin/env node
2
+ import{J as e,U as t,V as n,b as r,kt as i,q as a,u as o}from"./profile-storage-DlaupNlV.js";import{t as s}from"./confirm-BrJZbaxf.js";function c(e,t){return t?a(t)?`Next: entrydesk connectors connect ${e} --oauth`:t===`credentials`?`Next: entrydesk connectors connect ${e} --username <username> --password <password>`:`Next: entrydesk connectors connect ${e} --token <token>`:`This connector needs no authorization — there is no connect step.`}async function l(e){let{accessToken:t,workspaceId:n}=await o(),{connectors:i,nextKey:a}=await new r().getConnectors(t,n,{connectionStatus:e.all?`all`:`connected`,search:e.search,limit:e.limit,nextKey:e.nextKey});if(e.json){console.log(JSON.stringify({connectors:i,nextKey:a}));return}if(i.length===0){console.log(e.all?`No connectors found.`:`No connected connectors. Use --all to browse connectable ones.`);return}let s=e.all?`Connectors`:`Connected connectors`;console.log(`${s} (${i.length}):\n`);for(let e of i){let t=e.connected?`connected`:`not connected`;console.log(` ${e.name} (${t})`),console.log(` Provider: ${e.provider}`),console.log(` ID: ${e.id}`),e.authorization?.type&&console.log(` Auth: ${e.authorization.type}`),console.log(``)}a&&console.log(`More results: --next-key ${a}`)}async function u(e,t){let{accessToken:n,workspaceId:i}=await o(),{tools:a}=await new r().getConnectorTools(n,i,e);if(t.json){console.log(JSON.stringify({tools:a}));return}if(a.length===0){console.log(`This connector exposes no tools.`);return}console.log(`Tools (${a.length}):\n`);for(let e of a)console.log(` ${e.name} [${e.policy}]`),e.description&&console.log(` ${e.description}`)}async function d(e,t){let{accessToken:n,workspaceId:i}=await o(),a=new r;if(t.oauth){let{url:r}=await a.getConnectorAuthUrl(n,i,e);if(t.json){console.log(JSON.stringify({url:r}));return}console.log(`Open this URL in your browser to authorize the connector:`),console.log(` ${r}`),console.log(`
3
+ After you approve, the connection is saved automatically — re-run`),console.log("`entrydesk connectors` to confirm it shows as connected.");return}let s;t.token?s={token:t.token}:t.username&&t.password?s={username:t.username,password:t.password}:(console.error(`Provide --token, or --username and --password, or --oauth to connect.`),process.exit(1)),await a.connectConnectorToken(n,i,e,s),console.log(t.json?JSON.stringify({id:e,connected:!0}):`Connected connector ${e}.`)}async function f(a){let s=a.auth===`oauth`||a.auth===`google-iap`;s&&(!a.provider||!a.scope?.length)&&(console.error(`--auth ${a.auth} needs --provider and at least one --scope.`),process.exit(1)),!s&&(a.provider||a.scope?.length)&&(console.error(a.auth?`--provider and --scope apply only to --auth oauth and --auth google-iap, not --auth ${a.auth}.`:`--provider and --scope need --auth oauth or --auth google-iap.`),process.exit(1));let l=a.toolPrefix??e(a.name),u=t(l);if(u){let e=n[u];console.error(a.toolPrefix?`--tool-prefix ${e}.`:`The tool prefix derived from --name ("${l}") ${e}. Pass --tool-prefix explicitly.`),process.exit(1)}let{accessToken:d,workspaceId:f}=await o(),p=a.auth?{type:a.auth,...a.provider?{provider:a.provider}:{},...a.scope?.length?{scopes:a.scope}:{}}:void 0,m=await new r().createCustomConnector(d,f,{type:i.CUSTOM_REMOTE_URL,name:a.name,description:a.description,transport:a.transport,url:a.url,toolPrefix:l,...p?{authorization:p}:{}});if(a.json){console.log(JSON.stringify(m));return}console.log(`Created connector ${m.name}.`),console.log(` ID: ${m.id}`),console.log(` URL: ${m.url}`),console.log(` Tool prefix: ${m.toolPrefix}`),console.log(`\n${c(m.id,a.auth)}`)}async function p(e,t){let{accessToken:n,workspaceId:i}=await o();if(!t.yes&&(process.stdin.isTTY||(console.error(`Disconnecting connector ${e} needs confirmation. Re-run with --yes.`),process.exit(1)),!await s(`Disconnect connector ${e}?`))){console.log(`Aborted.`);return}await new r().disconnectConnector(n,i,e),console.log(t.json?JSON.stringify({id:e,disconnected:!0}):`Disconnected connector ${e}.`)}export{d as connectConnector,f as createConnector,p as disconnectConnector,u as listConnectorTools,l as listConnectors};
@@ -0,0 +1,16 @@
1
+ #!/usr/bin/env node
2
+ import e,{promises as t}from"node:fs";import n from"node:os";import r from"node:path";import{execFile as i}from"node:child_process";import a from"node:fs/promises";import{promisify as o}from"node:util";const s=o(i);async function c(){let t=r.join(n.tmpdir(),`entrydesk-clipboard-${Date.now()}.png`);try{let e=`
3
+ set imageData to the clipboard as "PNGf"
4
+ set fileRef to open for access POSIX file "${t}" with write permission
5
+ set eof fileRef to 0
6
+ write imageData to fileRef
7
+ close access fileRef
8
+ `;return await s(`osascript`,[`-e`,e]),(await a.stat(t)).size===0?void 0:{buffer:await a.readFile(t),mime:`image/png`}}catch{return}finally{try{e.existsSync(t)&&await a.unlink(t)}catch{}}}async function l(){try{let{stdout:e}=await s(`powershell.exe`,[`-command`,`
9
+ Add-Type -AssemblyName System.Windows.Forms
10
+ $img = [System.Windows.Forms.Clipboard]::GetImage()
11
+ if ($img) {
12
+ $ms = New-Object System.IO.MemoryStream
13
+ $img.Save($ms, [System.Drawing.Imaging.ImageFormat]::Png)
14
+ [System.Convert]::ToBase64String($ms.ToArray())
15
+ }
16
+ `]),t=e.trim();if(!t)return;let n=Buffer.from(t,`base64`);return n.length===0?void 0:{buffer:n,mime:`image/png`}}catch{return}}async function u(){try{let{stdout:e}=await s(`wl-paste`,[`-t`,`image/png`],{encoding:`buffer`,maxBuffer:50*1024*1024});if(e&&e.length>0)return{buffer:e,mime:`image/png`}}catch{}try{let{stdout:e}=await s(`xclip`,[`-selection`,`clipboard`,`-t`,`image/png`,`-o`],{encoding:`buffer`,maxBuffer:50*1024*1024});if(e&&e.length>0)return{buffer:e,mime:`image/png`}}catch{}}async function d(){let e=n.platform();if(e===`darwin`)return c();if(e===`win32`)return l();if(e===`linux`)return u()}const f=new Set(`.png,.jpg,.jpeg,.gif,.webp,.bmp,.ico,.heic,.heif,.pdf,.txt,.md,.rtf,.csv,.doc,.docx,.xls,.xlsx,.ppt,.pptx,.key,.pages,.numbers,.odt,.ods,.odp,.json,.xml,.yaml,.yml`.split(`,`));function p(e){let t=r.extname(e).toLowerCase();return f.has(t)}function m(e){return{".png":`image/png`,".jpg":`image/jpeg`,".jpeg":`image/jpeg`,".gif":`image/gif`,".webp":`image/webp`,".bmp":`image/bmp`,".ico":`image/x-icon`,".heic":`image/heic`,".heif":`image/heif`,".txt":`text/plain`,".md":`text/markdown`,".pdf":`application/pdf`,".csv":`text/csv`,".rtf":`application/rtf`,".doc":`application/msword`,".docx":`application/vnd.openxmlformats-officedocument.wordprocessingml.document`,".xls":`application/vnd.ms-excel`,".xlsx":`application/vnd.openxmlformats-officedocument.spreadsheetml.sheet`,".ppt":`application/vnd.ms-powerpoint`,".pptx":`application/vnd.openxmlformats-officedocument.presentationml.presentation`,".key":`application/vnd.apple.keynote`,".pages":`application/vnd.apple.pages`,".numbers":`application/vnd.apple.numbers`,".odt":`application/vnd.oasis.opendocument.text`,".ods":`application/vnd.oasis.opendocument.spreadsheet`,".odp":`application/vnd.oasis.opendocument.presentation`,".json":`application/json`,".xml":`application/xml`,".yaml":`text/yaml`,".yml":`text/yaml`}[r.extname(e).toLowerCase()]||`application/octet-stream`}function h(e){let t=e.trim();return t.startsWith(`file://`)&&(t=decodeURIComponent(t.slice(7))),(t.startsWith(`'`)&&t.endsWith(`'`)||t.startsWith(`"`)&&t.endsWith(`"`))&&(t=t.slice(1,-1)),t=t.replace(/\\(.)/g,`$1`),t}async function g(e){let t=h(e);if(/^https?:\/\//.test(t))return!1;try{return await a.access(t),!0}catch{return!1}}var _=class extends Error{problem;filePath;constructor(e,t){super(`${e}: ${t}`),this.problem=e,this.filePath=t,this.name=`LocalFileError`}};function v(e){return e instanceof Error&&`code`in e}function y(e){if(v(e)){if(e.code===`ENOENT`||e.code===`ENOTDIR`)return`missing`;if(e.code===`EISDIR`)return`not_a_file`}return`unreadable`}async function b(e){try{if(!(await t.stat(e)).isFile())throw new _(`not_a_file`,e);return{buffer:await t.readFile(e),filename:r.basename(e),mimeType:m(e)}}catch(t){throw t instanceof _?t:new _(y(t),e)}}export{h as a,g as i,b as n,d as o,p as r,_ as t};
@@ -0,0 +1,2 @@
1
+ #!/usr/bin/env node
2
+ function e(e){return`$${e.toFixed(2)}`}function t(e){if(e==null)return`—`;let t=Math.round(e),n=Math.floor(t/3600),r=Math.floor(t%3600/60),i=t%60,a=String(r).padStart(2,`0`),o=String(i).padStart(2,`0`);return n>0?`${n}:${a}:${o}`:`${r}:${o}`}function n(e){if(!e)return``;let t=new Date(e);return Number.isNaN(t.getTime())?e:t.toLocaleString()}function r(e,t){return`${e} ${t}${e===1?``:`s`}`}export{r as i,n,e as r,t};