@hailer/app-sdk 2.13.0 → 2.15.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -16,6 +16,7 @@ import { Admin } from './modules/admin';
16
16
  import { MetricsApi } from './modules/metrics';
17
17
  import { HttpApi } from './modules/http';
18
18
  import { CredentialApi } from './modules/credential';
19
+ import { ActionApi } from './modules/action';
19
20
  export interface Options {
20
21
  /** When connected is called the host is ready to receive calls */
21
22
  connected?: () => void;
@@ -55,6 +56,8 @@ export declare class HailerApi {
55
56
  http: HttpApi;
56
57
  /** Securely store and retrieve credentials (API keys, username/password pairs, etc.) */
57
58
  credential: CredentialApi;
59
+ /** Trigger workspace actions and read back their runs */
60
+ action: ActionApi;
58
61
  /** All public endpoints, which can be accessed while not logged in to Hailer. */
59
62
  public: HailerPublicApi;
60
63
  /** User interface commands */
package/lib/index.d.ts CHANGED
@@ -1,3 +1,4 @@
1
1
  export { HailerApi } from './hailer-api';
2
2
  export * from './types';
3
3
  export * from './modules/metrics';
4
+ export * from './modules/file';
@@ -0,0 +1,22 @@
1
+ import { Client } from '@wishcore/wish-rpc';
2
+ import { ActionRun, ActionTriggerTarget } from '../types';
3
+ export declare class ActionApi {
4
+ private client;
5
+ constructor(client: Client);
6
+ /**
7
+ * Triggers an action. Waits up to 25 s; a longer run is returned still
8
+ * in progress and must be polled with `getRun`.
9
+ *
10
+ * @param target one of `actionId` or `key`, plus optional `workspaceId` (defaults to the app's workspace)
11
+ * @param input delivered as the payload of a `manual` event
12
+ *
13
+ * @throws {HailerError} if the action is not found or the user may not trigger it
14
+ */
15
+ trigger(target: ActionTriggerTarget, input?: unknown): Promise<ActionRun>;
16
+ /**
17
+ * Gets one run. Non-admins can only read runs they triggered.
18
+ *
19
+ * @throws {HailerError} if the run is not found or the user may not read it
20
+ */
21
+ getRun(runId: string): Promise<ActionRun>;
22
+ }
@@ -10,9 +10,74 @@ export interface FileResponse {
10
10
  export interface UploadOptions {
11
11
  /** Whether the file should be publicly accessible */
12
12
  isPublic?: boolean;
13
+ /**
14
+ * Scope the file to the app's workspace (irreversible). A scoped file is readable only by its owner,
15
+ * workspace admins and its readers; set readers with `setReaders`. Cannot be combined with `isPublic`,
16
+ * and not available to guests. This is the only way to scope a file.
17
+ */
18
+ scoped?: boolean;
19
+ }
20
+ /** Owner, workspace and readers of a file. `workspaceId` and `readers` are absent for an unscoped file. */
21
+ export interface FileScope {
22
+ _id: string;
23
+ /** Owner of the file */
24
+ uid: string;
25
+ /** Workspace the file is scoped to */
26
+ workspaceId?: string;
27
+ /** Reader entries, see `FileApi.readers` */
28
+ readers?: string[];
29
+ }
30
+ export interface FileListOptions {
31
+ /** One keyword or up to 20. A keyword matches a case-insensitive substring of the file name or of any tag. */
32
+ search?: string | string[];
33
+ /** `all` (default) requires every keyword to match, `any` at least one */
34
+ matchType?: 'all' | 'any';
35
+ skip?: number;
36
+ /** 1-100, default 100 */
37
+ limit?: number;
38
+ sortBy?: 'name' | 'created';
39
+ reverse?: boolean;
13
40
  }
41
+ export interface ScopedFile {
42
+ _id: string;
43
+ name: string;
44
+ /** Owner of the file */
45
+ uid: string;
46
+ workspaceId: string;
47
+ type?: string;
48
+ length?: number;
49
+ created?: number;
50
+ updated?: number;
51
+ tags?: string[];
52
+ }
53
+ export interface FileListResponse {
54
+ files: ScopedFile[];
55
+ hasMore: boolean;
56
+ total: number;
57
+ }
58
+ /**
59
+ * Builders for `setReaders` entries. Only full members of the app's workspace ever match a reader:
60
+ * guests never do, not even through a team or a `user_` entry.
61
+ */
62
+ export declare const fileReaders: {
63
+ /** Every full member of the workspace */
64
+ workspace: (workspaceId: string) => string;
65
+ /** The full members of a team; guests in the team are skipped */
66
+ team: (teamId: string) => string;
67
+ /** A single full member; a guest is rejected */
68
+ user: (userId: string) => string;
69
+ };
14
70
  export declare class FileApi {
15
71
  private client;
72
+ /** Builders for `setReaders` entries: `readers.workspace(id)`, `readers.team(id)`, `readers.user(id)` */
73
+ readonly readers: {
74
+ /** Every full member of the workspace */
75
+ workspace: (workspaceId: string) => string;
76
+ /** The full members of a team; guests in the team are skipped */
77
+ team: (teamId: string) => string;
78
+ /** A single full member; a guest is rejected */
79
+ user: (userId: string) => string;
80
+ };
16
81
  constructor(client: Client);
17
82
  /**
18
83
  * Gets a file by ID and returns it as base64 data
@@ -39,7 +104,8 @@ export declare class FileApi {
39
104
  /**
40
105
  * Appends tags to a file, deduping against the existing list.
41
106
  *
42
- * Tags are case-sensitive free-form strings (trimmed, 1-64 characters, max 50 per file).
107
+ * Tags are case-sensitive free-form strings (trimmed, 1-64 characters, max 50 per file). On a scoped
108
+ * file only its owner or a workspace admin may change tags; readers can read them but not edit.
43
109
  *
44
110
  * @param fileId The ID of the file to tag
45
111
  * @param tags Tags to add
@@ -51,6 +117,7 @@ export declare class FileApi {
51
117
  addTags(fileId: string, tags: string[]): Promise<string[]>;
52
118
  /**
53
119
  * Removes tags from a file by case-sensitive exact match. Tags not present are silently ignored.
120
+ * On a scoped file only its owner or a workspace admin may change tags.
54
121
  *
55
122
  * @param fileId The ID of the file to untag
56
123
  * @param tags Tags to remove
@@ -60,5 +127,74 @@ export declare class FileApi {
60
127
  * @throws {HailerError} if validation fails or the file cannot be edited
61
128
  */
62
129
  removeTags(fileId: string, tags: string[]): Promise<string[]>;
130
+ /**
131
+ * Gets the owner, workspace and readers of a file you can read. Use it as an "is this file scoped?"
132
+ * probe: `workspaceId` is absent for an unscoped file.
133
+ *
134
+ * @param fileId The ID of the file
135
+ *
136
+ * @throws {HailerError} if the file does not exist or you cannot read it (guests cannot read scoped
137
+ * files), or if it is scoped to a workspace other than the app's (403)
138
+ */
139
+ getScope(fileId: string): Promise<FileScope>;
140
+ /**
141
+ * Replaces the readers of a file scoped to the app's workspace. Files are scoped only at upload
142
+ * (`upload(..., { scoped: true })`); an unscoped file is refused (403) rather than scoped here.
143
+ *
144
+ * Requires being the file's owner (while a full member) or a workspace admin; not available to guests.
145
+ * A non-admin may only add teams they belong to. Every entry must belong to the app's workspace, and
146
+ * `user_` must name a full member, not a guest.
147
+ *
148
+ * @param fileId The ID of the file
149
+ * @param readers The complete new reader list (max 100); build entries with `readers.*`. Send `[]` to
150
+ * leave only the owner and workspace admins.
151
+ *
152
+ * @returns The resulting scope
153
+ *
154
+ * @throws {HailerError} if validation fails, the file is not scoped to the app's workspace, or you may
155
+ * not manage it
156
+ */
157
+ setReaders(fileId: string, readers: string[]): Promise<FileScope>;
158
+ /**
159
+ * Lists the scoped files of the app's workspace that the user can read: their own, those they are a
160
+ * reader of, or all of them for a workspace admin. Unscoped files are not listed.
161
+ *
162
+ * @param options Search by name/tags and paging
163
+ *
164
+ * @returns A page of files, whether more exist, and the total match count
165
+ *
166
+ * @throws {HailerError} if validation fails, or for a guest
167
+ */
168
+ list(options?: FileListOptions): Promise<FileListResponse>;
169
+ /**
170
+ * Transfers a file scoped to the app's workspace to another full member of it (not a guest). Workspace
171
+ * admins only. The previous owner loses access unless they are a reader.
172
+ *
173
+ * @param fileId The ID of the file
174
+ * @param userId The new owner
175
+ *
176
+ * @throws {HailerError} if you are not a workspace admin, the file is not scoped to the app's workspace,
177
+ * or the user is not a full member
178
+ */
179
+ setOwner(fileId: string, userId: string): Promise<boolean>;
180
+ /**
181
+ * Permanently deletes files scoped to the app's workspace that you own. Ids of files owned by someone
182
+ * else are skipped without an error, even for a workspace admin; re-list to see what is left. If any id
183
+ * is not a file scoped to the app's workspace, the whole call is refused (403) and nothing is removed:
184
+ * an app can never delete the user's other files.
185
+ *
186
+ * @param fileIds 1-100 file ids
187
+ *
188
+ * @returns `true` once the request is processed
189
+ *
190
+ * @throws {HailerError} if validation fails, or any file is not scoped to the app's workspace
191
+ */
192
+ remove(fileIds: string[]): Promise<boolean>;
193
+ /**
194
+ * An older Hailer ignores the `scoped` upload argument and stores the file unscoped. Fail loudly rather
195
+ * than hand back a file the app believes is restricted. Only a missing `file.getScope` (wish-rpc code
196
+ * 300) or an unscoped result means an older Hailer; any other error is passed on as is.
197
+ */
198
+ private assertScoped;
63
199
  private toBase64;
64
200
  }
@@ -25,6 +25,6 @@ export declare class PermissionApi {
25
25
  * @throws {HailerError} if the request fails
26
26
  */
27
27
  map: ({ workspaceId }?: {
28
- workspaceId?: string;
28
+ workspaceId?: string | undefined;
29
29
  }) => Promise<PermissionMap>;
30
30
  }
package/lib/types.d.ts CHANGED
@@ -453,6 +453,42 @@ export interface Settings {
453
453
  theme: 'light' | 'dark';
454
454
  preferredLanguages: string[];
455
455
  }
456
+ /** One of actionId or key. workspaceId is optional, needed with a key in a private app. */
457
+ export interface ActionTriggerTarget {
458
+ actionId?: string;
459
+ key?: string;
460
+ workspaceId?: string;
461
+ }
462
+ /** `queued` and `running` are in progress, the rest are final. */
463
+ export type ActionRunStatus = 'queued' | 'running' | 'completed' | 'runtime_error' | 'timeout' | 'output_limit' | 'infra_error' | 'cancelled' | 'config_error' | 'denied';
464
+ export type ActionEvent = 'manual' | 'phase.activity-created' | 'phase.activity-updated' | 'phase.activity-moved' | 'workspace.member-joined' | 'workspace.member-left' | 'schedule.run';
465
+ export declare const ACTION_RUN_ACTIVE_STATUSES: ActionRunStatus[];
466
+ /** One run of an action. Fields marked "admins only" are absent for other users. */
467
+ export interface ActionRun {
468
+ _id: string;
469
+ actionId: string;
470
+ workspaceId: string;
471
+ event: ActionEvent;
472
+ /** Absent on scheduled runs */
473
+ triggeredBy?: string;
474
+ status: ActionRunStatus;
475
+ exitCode?: number | null;
476
+ /** Return value of `execute(input)` */
477
+ output?: unknown;
478
+ /** Detail for abnormal statuses */
479
+ message?: string | null;
480
+ wallTimeMs?: number | null;
481
+ created: number;
482
+ finishedAt?: number | null;
483
+ /** Admins only */
484
+ arguments?: string;
485
+ /** Admins only */
486
+ stdout?: string | null;
487
+ /** Admins only */
488
+ stderr?: string | null;
489
+ /** Admins only */
490
+ execRunId?: string | null;
491
+ }
456
492
  export type CredentialType = 'basic' | 'apiKey' | 'custom';
457
493
  export interface CredentialBasicData {
458
494
  username: string;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@hailer/app-sdk",
3
- "version": "2.13.0",
3
+ "version": "2.15.0",
4
4
  "description": "SDK for building Hailer Apps",
5
5
  "author": "",
6
6
  "license": "ISC",