codatta-frontier-sdk 0.1.8 → 0.1.9
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +121 -27
- package/dist/index.cjs +1 -1
- package/dist/index.d.ts +9 -2
- package/dist/index.mjs +2 -1
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -1,80 +1,174 @@
|
|
|
1
1
|
# Codatta Frontier SDK
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
API client SDK for the Codatta Frontier platform.
|
|
4
4
|
|
|
5
|
-
##
|
|
5
|
+
## Installation
|
|
6
6
|
|
|
7
7
|
```bash
|
|
8
8
|
npm install codatta-frontier-sdk
|
|
9
9
|
```
|
|
10
10
|
|
|
11
|
-
>
|
|
11
|
+
> **Note**: `axios` is a peerDependency. Make sure `axios >= 1.0.0` is installed in your project.
|
|
12
12
|
|
|
13
|
-
##
|
|
13
|
+
## Usage
|
|
14
14
|
|
|
15
15
|
```typescript
|
|
16
16
|
import { FrontierSDK } from 'codatta-frontier-sdk'
|
|
17
17
|
|
|
18
18
|
const sdk = new FrontierSDK()
|
|
19
19
|
|
|
20
|
-
//
|
|
20
|
+
// Get task detail
|
|
21
21
|
const taskDetail = await sdk.getTaskDetail('task-id')
|
|
22
22
|
|
|
23
|
-
//
|
|
23
|
+
// Get task list
|
|
24
24
|
const taskList = await sdk.getTaskList({
|
|
25
25
|
frontier_id: 'frontier-id',
|
|
26
26
|
page_num: 1,
|
|
27
27
|
page_size: 10
|
|
28
28
|
})
|
|
29
29
|
|
|
30
|
-
//
|
|
30
|
+
// Submit a task
|
|
31
31
|
const result = await sdk.submitTask('task-id', { field: 'value' })
|
|
32
32
|
|
|
33
|
-
//
|
|
33
|
+
// Get submission list
|
|
34
34
|
const submissions = await sdk.getSubmissionList({
|
|
35
35
|
page_num: 1,
|
|
36
36
|
page_size: 10,
|
|
37
37
|
frontier_id: 'frontier-id'
|
|
38
38
|
})
|
|
39
39
|
|
|
40
|
-
//
|
|
40
|
+
// Get Frontier info
|
|
41
41
|
const frontierInfo = await sdk.getFrontierInfo('frontier-id')
|
|
42
42
|
|
|
43
|
-
//
|
|
43
|
+
// Get submission detail
|
|
44
44
|
const submissionDetail = await sdk.getSubmissionDetail('submission-id')
|
|
45
45
|
|
|
46
|
-
//
|
|
46
|
+
// Upload a file (with optional progress callback)
|
|
47
47
|
const uploadResult = await sdk.uploadFile(file, (event) => {
|
|
48
48
|
console.log(`Upload progress: ${Math.round((event.loaded * 100) / (event.total ?? 1))}%`)
|
|
49
49
|
})
|
|
50
|
+
|
|
51
|
+
// Get a single spec task info
|
|
52
|
+
const specTask = await sdk.getSpecTaskInfo('task-id')
|
|
53
|
+
|
|
54
|
+
// Get multiple spec tasks info at once (comma-separated IDs)
|
|
55
|
+
const specTasks = await sdk.getSpecTaskInfos('task-id-1,task-id-2,task-id-3')
|
|
56
|
+
|
|
57
|
+
// Submit a spec task (status: 1 = reject, 2 = accept)
|
|
58
|
+
const specResult = await sdk.submitSpecTask('task-id', 'optional content', 2)
|
|
59
|
+
|
|
60
|
+
// Request a verification code sent to an email
|
|
61
|
+
const code = await sdk.getVerificationCode({ email: 'user@example.com' })
|
|
62
|
+
|
|
63
|
+
// Verify email against a task requirement
|
|
64
|
+
const check = await sdk.checkEmail({
|
|
65
|
+
email: 'user@example.com',
|
|
66
|
+
code: '123456',
|
|
67
|
+
task_id: 'task-id'
|
|
68
|
+
})
|
|
69
|
+
if (check.flag) {
|
|
70
|
+
console.log('Email verified successfully')
|
|
71
|
+
} else {
|
|
72
|
+
console.warn('Verification failed:', check.info)
|
|
73
|
+
}
|
|
50
74
|
```
|
|
51
75
|
|
|
52
76
|
## API
|
|
53
77
|
|
|
54
78
|
### `new FrontierSDK()`
|
|
55
79
|
|
|
56
|
-
|
|
80
|
+
Creates an SDK instance. No parameters required — the SDK handles the following automatically:
|
|
81
|
+
|
|
82
|
+
- **Token**: Retrieved from `cookie` (`auth`) or `localStorage` (`auth`)
|
|
83
|
+
|
|
84
|
+
### Methods
|
|
85
|
+
|
|
86
|
+
| Method | Description |
|
|
87
|
+
|--------|------------|
|
|
88
|
+
| `getTaskDetail(taskId)` | Get task detail |
|
|
89
|
+
| `submitTask(taskId, data)` | Submit task data |
|
|
90
|
+
| `getTaskList(params)` | Get paginated task list |
|
|
91
|
+
| `getSubmissionList(params)` | Get paginated submission list |
|
|
92
|
+
| `getFrontierInfo(frontierId)` | Get Frontier detail |
|
|
93
|
+
| `getSubmissionDetail(submissionId)` | Get submission detail |
|
|
94
|
+
| `uploadFile(file, onProgress?)` | Upload a file |
|
|
95
|
+
| `getSpecTaskInfo(taskId)` | Get single spec task info |
|
|
96
|
+
| `getSpecTaskInfos(taskIds)` | Get multiple spec tasks info |
|
|
97
|
+
| `submitSpecTask(taskId, content?, status?)` | Submit or update a spec task |
|
|
98
|
+
| `getVerificationCode(params)` | Request a verification code |
|
|
99
|
+
| `checkEmail(params)` | Verify email against a task requirement |
|
|
100
|
+
|
|
101
|
+
### Method Details
|
|
102
|
+
|
|
103
|
+
#### `getTaskList(params)`
|
|
104
|
+
|
|
105
|
+
| Parameter | Type | Required | Description |
|
|
106
|
+
|-----------|------|----------|-------------|
|
|
107
|
+
| `frontier_id` | `string` | Yes | The frontier to query tasks for |
|
|
108
|
+
| `page_num` | `number` | Yes | Page number (1-based) |
|
|
109
|
+
| `page_size` | `number` | Yes | Number of tasks per page |
|
|
110
|
+
| `task_types` | `string` | No | Comma-separated task type filter (e.g. `'submission,validation'`) |
|
|
111
|
+
|
|
112
|
+
#### `getSubmissionList(params)`
|
|
113
|
+
|
|
114
|
+
| Parameter | Type | Required | Description |
|
|
115
|
+
|-----------|------|----------|-------------|
|
|
116
|
+
| `page_num` | `number` | Yes | Page number (1-based) |
|
|
117
|
+
| `page_size` | `number` | Yes | Number of items per page |
|
|
118
|
+
| `frontier_id` | `string` | No | Filter by frontier ID |
|
|
119
|
+
| `task_ids` | `string` | No | Comma-separated list of task IDs to filter by |
|
|
120
|
+
|
|
121
|
+
#### `submitSpecTask(taskId, content?, status?)`
|
|
122
|
+
|
|
123
|
+
| Parameter | Type | Required | Description |
|
|
124
|
+
|-----------|------|----------|-------------|
|
|
125
|
+
| `taskId` | `string` | Yes | Unique task ID |
|
|
126
|
+
| `content` | `string` | No | Optional freeform content to attach |
|
|
127
|
+
| `status` | `1 \| 2` | No | `1` = reject, `2` = accept (default `2`) |
|
|
128
|
+
|
|
129
|
+
#### `getVerificationCode(params)`
|
|
130
|
+
|
|
131
|
+
| Parameter | Type | Required | Description |
|
|
132
|
+
|-----------|------|----------|-------------|
|
|
133
|
+
| `account_type` | `string` | No | Account type: `'email'` (default) \| `'block_chain'` |
|
|
134
|
+
| `email` | `string` | No | Target email address. Required when `account_type` is `'email'` |
|
|
135
|
+
| `opt` | `string` | No | Operation type: `'verify'` (default) \| `'vivolight'` |
|
|
136
|
+
|
|
137
|
+
Returns the verification code string. Pass it to `checkEmail` to complete verification.
|
|
138
|
+
|
|
139
|
+
#### `checkEmail(params)`
|
|
140
|
+
|
|
141
|
+
| Parameter | Type | Required | Description |
|
|
142
|
+
|-----------|------|----------|-------------|
|
|
143
|
+
| `email` | `string` | Yes | Email address to verify |
|
|
144
|
+
| `code` | `string` | Yes | Verification code from `getVerificationCode` |
|
|
145
|
+
| `task_id` | `string` | Yes | The task ID requiring email verification |
|
|
146
|
+
|
|
147
|
+
Returns `{ flag: boolean; info: string }` — `flag: true` if verification passed; `flag: false` with a reason in `info` otherwise.
|
|
57
148
|
|
|
58
|
-
|
|
59
|
-
- **Channel**: 根据 `userAgent` / `location.hash` 自动判断(App 或 Web)
|
|
60
|
-
- **Device**: 根据 `userAgent` 自动判断(移动端或 PC)
|
|
149
|
+
## Types
|
|
61
150
|
|
|
62
|
-
|
|
151
|
+
Key types exported from `codatta-frontier-sdk`:
|
|
63
152
|
|
|
64
|
-
|
|
|
65
|
-
|
|
66
|
-
| `
|
|
67
|
-
| `
|
|
68
|
-
| `
|
|
69
|
-
| `
|
|
70
|
-
| `
|
|
71
|
-
| `
|
|
72
|
-
| `
|
|
153
|
+
| Type | Description |
|
|
154
|
+
|------|-------------|
|
|
155
|
+
| `Response<T>` | Standard API response wrapper |
|
|
156
|
+
| `PaginationResponse<T>` | Paginated response wrapper |
|
|
157
|
+
| `TPagination` | Common pagination parameters (`page_num`, `page_size`) |
|
|
158
|
+
| `TaskDetail` | Full details of a frontier task, including display data, submission state, and reward info |
|
|
159
|
+
| `FrontierItemType` | Frontier metadata, configuration, and reward structure |
|
|
160
|
+
| `SubmissionRecord` | Historical submission record returned by the submission list / detail endpoints |
|
|
161
|
+
| `TaskInfo` | Spec task info used by the `/v2/spec/task/*` endpoints |
|
|
162
|
+
| `TaskRewardInfo` | Reward configuration attached to a task |
|
|
163
|
+
| `RankingGrade` | Quality grade: `'S' \| 'A' \| 'B' \| 'C' \| 'D'` |
|
|
164
|
+
| `TaskType` | Task category: `'submission' \| 'validation'` |
|
|
165
|
+
| `ActiveStatus` | Frontier status: `'ACTIVE' \| 'INACTIVE' \| 'COMPLETED'` |
|
|
166
|
+
| `MediaName` | Social media identifiers used in frontier media links |
|
|
73
167
|
|
|
74
|
-
##
|
|
168
|
+
## Build
|
|
75
169
|
|
|
76
170
|
```bash
|
|
77
171
|
npm run build
|
|
78
172
|
```
|
|
79
173
|
|
|
80
|
-
|
|
174
|
+
Outputs ESM (`dist/index.mjs`), CJS (`dist/index.cjs`), and type declaration files.
|
package/dist/index.cjs
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
"use strict";Object.defineProperty(exports,Symbol.toStringTag,{value:"Module"});const d=require("axios");/*! js-cookie v3.0.5 | MIT */function u(r){for(var e=1;e<arguments.length;e++){var t=arguments[e];for(var o in t)r[o]=t[o]}return r}var h={read:function(r){return r[0]==='"'&&(r=r.slice(1,-1)),r.replace(/(%[\dA-F]{2})+/gi,decodeURIComponent)},write:function(r){return encodeURIComponent(r).replace(/%(2[346BF]|3[AC-F]|40|5[BDE]|60|7[BCD])/g,decodeURIComponent)}};function l(r,e){function t(s,i,n){if(!(typeof document>"u")){n=u({},e,n),typeof n.expires=="number"&&(n.expires=new Date(Date.now()+n.expires*864e5)),n.expires&&(n.expires=n.expires.toUTCString()),s=encodeURIComponent(s).replace(/%(2[346B]|5E|60|7C)/g,decodeURIComponent).replace(/[()]/g,escape);var a="";for(var c in n)n[c]&&(a+="; "+c,n[c]!==!0&&(a+="="+n[c].split(";")[0]));return document.cookie=s+"="+r.write(i,s)+a}}function o(s){if(!(typeof document>"u"||arguments.length&&!s)){for(var i=document.cookie?document.cookie.split("; "):[],n={},a=0;a<i.length;a++){var c=i[a].split("="),m=c.slice(1).join("=");try{var p=decodeURIComponent(c[0]);if(n[p]=r.read(m,p),s===p)break}catch{}}return s?n[s]:n}}return Object.create({set:t,get:o,remove:function(s,i){t(s,"",u({},i,{expires:-1}))},withAttributes:function(s){return l(this.converter,u({},this.attributes,s))},withConverter:function(s){return l(u({},this.converter,s),this.attributes)}},{attributes:{value:Object.freeze(e)},converter:{value:Object.freeze(r)}})}var g=l(h,{path:"/"});class v{constructor(){this.request=d.create({timeout:3e4}),this.setupRequestInterceptor(),this.setupResponseInterceptor()}setupRequestInterceptor(){this.request.interceptors.request.use(e=>{var n;const t=navigator.userAgent.toLowerCase(),o=t.includes("codatta")||((n=location.hash)==null?void 0:n.toLowerCase().includes("codatta")),s=g.get("auth")||localStorage.getItem("auth"),i=/android|webos|iphone|ipad|ipod|blackberry|iemobile|opera mini/i.test(t);return s&&(e.headers.token=s),e.headers.channel=o?"codatta-ios-app":"codatta-platform-website",e.headers.device=i?"mobile":"web",e})}setupResponseInterceptor(){this.request.interceptors.response.use(e=>{const t=e.data,o=Object.getOwnPropertyNames(t).includes("code"),s=Object.getOwnPropertyNames(t).includes("success");return o&&(t==null?void 0:t.code)!=="000000"?Promise.reject(new d.AxiosError(t==null?void 0:t.message,t==null?void 0:t.code,e.config,e.request,e)):s&&(t==null?void 0:t.success)!==!0?Promise.reject(new d.AxiosError(t==null?void 0:t.errorMessage,t==null?void 0:t.errorCode,e.config,e.request,e)):e},e=>{if(e.status===401){localStorage.removeItem("uid"),localStorage.removeItem("token"),localStorage.removeItem("auth");const t=new URL(window.location.href),o=t.pathname+t.search;window.location.href=`/account/signin?from=${encodeURIComponent(o)}`}return Promise.reject(e)})}async getTaskDetail(e){return(await this.request.post("/api/v2/frontier/task/detail",{task_id:e})).data}async submitTask(e,t){return(await this.request.post("/api/v2/frontier/task/submit",{task_id:e,data_submission:{data:t,task_id:e}})).data}async getTaskList(e){return(await this.request.post("/api/v2/frontier/task/list",e)).data}async getSubmissionList(e){return(await this.request.post("/api/v2/submission/list",e)).data}async getFrontierInfo(e){return(await this.request.get(`/api/v2/frontier/info?frontier_id=${e}`)).data}async getSubmissionDetail(e){return(await this.request.get("/api/v2/submission/user/detail",{params:{submission_id:e}})).data}async uploadFile(e,t){const o=new FormData;return o.append("file",e),(await this.request.post("/api/file/upload",o,{params:{content_type:e.type},onUploadProgress:t})).data}async getSpecTaskInfo(e){return(await this.request.get(`/api/v2/spec/task/info?task_id=${e}`)).data}async getSpecTaskInfos(e){return(await this.request.get(`/api/v2/spec/task/infos?task_ids=${e}`)).data}async submitSpecTask(e,t,o){return(await this.request.post("/api/v2/spec/task/submit",{task_id:e,status:o??2,content:t})).data}async getVerificationCode({account_type:e,email:t,opt:o}){return(await this.request.post("/api/v2/user/get_code",{account_type:e??"email",email:t??"",opt:o??"verify"})).data.data}async checkEmail({email:e,code:t,task_id:o}){return(await this.request.post("/api/v2/frontier/email/check",{email:e,code:t,task_id:o})).data.data}}var f=(r=>(r.TWITTER="x",r.TELEGRAM="telegram",r.DISCORD="discord",r.WEBSITE="website",r.DOC="doc",r))(f||{});exports.FrontierSDK=v;exports.MediaName=f;
|
|
1
|
+
"use strict";Object.defineProperty(exports,Symbol.toStringTag,{value:"Module"});const d=require("axios");/*! js-cookie v3.0.5 | MIT */function u(r){for(var e=1;e<arguments.length;e++){var t=arguments[e];for(var o in t)r[o]=t[o]}return r}var h={read:function(r){return r[0]==='"'&&(r=r.slice(1,-1)),r.replace(/(%[\dA-F]{2})+/gi,decodeURIComponent)},write:function(r){return encodeURIComponent(r).replace(/%(2[346BF]|3[AC-F]|40|5[BDE]|60|7[BCD])/g,decodeURIComponent)}};function l(r,e){function t(s,i,n){if(!(typeof document>"u")){n=u({},e,n),typeof n.expires=="number"&&(n.expires=new Date(Date.now()+n.expires*864e5)),n.expires&&(n.expires=n.expires.toUTCString()),s=encodeURIComponent(s).replace(/%(2[346B]|5E|60|7C)/g,decodeURIComponent).replace(/[()]/g,escape);var a="";for(var c in n)n[c]&&(a+="; "+c,n[c]!==!0&&(a+="="+n[c].split(";")[0]));return document.cookie=s+"="+r.write(i,s)+a}}function o(s){if(!(typeof document>"u"||arguments.length&&!s)){for(var i=document.cookie?document.cookie.split("; "):[],n={},a=0;a<i.length;a++){var c=i[a].split("="),m=c.slice(1).join("=");try{var p=decodeURIComponent(c[0]);if(n[p]=r.read(m,p),s===p)break}catch{}}return s?n[s]:n}}return Object.create({set:t,get:o,remove:function(s,i){t(s,"",u({},i,{expires:-1}))},withAttributes:function(s){return l(this.converter,u({},this.attributes,s))},withConverter:function(s){return l(u({},this.converter,s),this.attributes)}},{attributes:{value:Object.freeze(e)},converter:{value:Object.freeze(r)}})}var g=l(h,{path:"/"});class v{constructor(){this.request=d.create({timeout:3e4}),this.setupRequestInterceptor(),this.setupResponseInterceptor()}setupRequestInterceptor(){this.request.interceptors.request.use(e=>{var n;const t=navigator.userAgent.toLowerCase(),o=t.includes("codatta")||((n=location.hash)==null?void 0:n.toLowerCase().includes("codatta")),s=g.get("auth")||localStorage.getItem("auth"),i=/android|webos|iphone|ipad|ipod|blackberry|iemobile|opera mini/i.test(t);return s&&(e.headers.token=s),e.headers.channel=o?"codatta-ios-app":"codatta-platform-website",e.headers.device=i?"mobile":"web",e})}setupResponseInterceptor(){this.request.interceptors.response.use(e=>{const t=e.data,o=Object.getOwnPropertyNames(t).includes("code"),s=Object.getOwnPropertyNames(t).includes("success");return o&&(t==null?void 0:t.code)!=="000000"?Promise.reject(new d.AxiosError(t==null?void 0:t.message,t==null?void 0:t.code,e.config,e.request,e)):s&&(t==null?void 0:t.success)!==!0?Promise.reject(new d.AxiosError(t==null?void 0:t.errorMessage,t==null?void 0:t.errorCode,e.config,e.request,e)):e},e=>{if(e.status===401){localStorage.removeItem("uid"),localStorage.removeItem("token"),localStorage.removeItem("auth");const t=new URL(window.location.href),o=t.pathname+t.search;window.location.href=`/account/signin?from=${encodeURIComponent(o)}`}return Promise.reject(e)})}async getTaskDetail(e){return(await this.request.post("/api/v2/frontier/task/detail",{task_id:e})).data}async submitTask(e,t,o){return(await this.request.post("/api/v2/frontier/task/submit",{task_id:e,data_submission:{uid:o,data:t,task_id:e}})).data}async getTaskList(e){return(await this.request.post("/api/v2/frontier/task/list",e)).data}async getSubmissionList(e){return(await this.request.post("/api/v2/submission/list",e)).data}async getFrontierInfo(e){return(await this.request.get(`/api/v2/frontier/info?frontier_id=${e}`)).data}async getSubmissionDetail(e){return(await this.request.get("/api/v2/submission/user/detail",{params:{submission_id:e}})).data}async uploadFile(e,t){const o=new FormData;return o.append("file",e),(await this.request.post("/api/file/upload",o,{params:{content_type:e.type},onUploadProgress:t})).data}async getSpecTaskInfo(e){return(await this.request.get(`/api/v2/spec/task/info?task_id=${e}`)).data}async getSpecTaskInfos(e){return(await this.request.get(`/api/v2/spec/task/infos?task_ids=${e}`)).data}async submitSpecTask(e,t,o){return(await this.request.post("/api/v2/spec/task/submit",{task_id:e,status:o??2,content:t})).data}async getVerificationCode({account_type:e,email:t,opt:o}){return(await this.request.post("/api/v2/user/get_code",{account_type:e??"email",email:t??"",opt:o??"verify"})).data.data}async checkEmail({email:e,code:t,task_id:o}){return(await this.request.post("/api/v2/frontier/email/check",{email:e,code:t,task_id:o})).data.data}}var f=(r=>(r.TWITTER="x",r.TELEGRAM="telegram",r.DISCORD="discord",r.WEBSITE="website",r.DOC="doc",r))(f||{});exports.FrontierSDK=v;exports.MediaName=f;
|
package/dist/index.d.ts
CHANGED
|
@@ -24,6 +24,13 @@ export declare interface CMUDataRequirements {
|
|
|
24
24
|
};
|
|
25
25
|
}
|
|
26
26
|
|
|
27
|
+
export declare interface FashionQuestion {
|
|
28
|
+
content: object;
|
|
29
|
+
image_url: string;
|
|
30
|
+
source_type: string;
|
|
31
|
+
uid: string;
|
|
32
|
+
}
|
|
33
|
+
|
|
27
34
|
export declare interface FrontierItemType {
|
|
28
35
|
id?: number;
|
|
29
36
|
name: string;
|
|
@@ -66,7 +73,7 @@ export declare class FrontierSDK {
|
|
|
66
73
|
* @param data - Submission payload. Shape depends on the task template.
|
|
67
74
|
* @returns Updated task detail after submission.
|
|
68
75
|
*/
|
|
69
|
-
submitTask(taskId: string, data: object): Promise<Response_2<TaskDetail>>;
|
|
76
|
+
submitTask(taskId: string, data: object, uid?: string): Promise<Response_2<TaskDetail>>;
|
|
70
77
|
/**
|
|
71
78
|
* Fetch a paginated list of tasks for a given frontier.
|
|
72
79
|
* @param params.frontier_id - The frontier to query tasks for.
|
|
@@ -276,7 +283,7 @@ export declare interface TaskDetail {
|
|
|
276
283
|
web_template_url?: string;
|
|
277
284
|
app_template_url?: string;
|
|
278
285
|
};
|
|
279
|
-
questions?: CMUDataRequirements[];
|
|
286
|
+
questions?: CMUDataRequirements[] | FashionQuestion[];
|
|
280
287
|
data_submission?: {
|
|
281
288
|
[key: string]: unknown;
|
|
282
289
|
lifelog_report?: string;
|
package/dist/index.mjs
CHANGED
|
@@ -115,10 +115,11 @@ class y {
|
|
|
115
115
|
* @param data - Submission payload. Shape depends on the task template.
|
|
116
116
|
* @returns Updated task detail after submission.
|
|
117
117
|
*/
|
|
118
|
-
async submitTask(e, t) {
|
|
118
|
+
async submitTask(e, t, o) {
|
|
119
119
|
return (await this.request.post("/api/v2/frontier/task/submit", {
|
|
120
120
|
task_id: e,
|
|
121
121
|
data_submission: {
|
|
122
|
+
uid: o,
|
|
122
123
|
data: t,
|
|
123
124
|
task_id: e
|
|
124
125
|
}
|