gentask 0.1.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.
- package/LICENSE +21 -0
- package/README.md +66 -0
- package/dist/cli.d.ts +19 -0
- package/dist/cli.js +225 -0
- package/dist/cli.js.map +1 -0
- package/dist/config.d.ts +37 -0
- package/dist/config.js +72 -0
- package/dist/config.js.map +1 -0
- package/dist/format.d.ts +19 -0
- package/dist/format.js +95 -0
- package/dist/format.js.map +1 -0
- package/dist/gentask-client.d.ts +48 -0
- package/dist/gentask-client.js +104 -0
- package/dist/gentask-client.js.map +1 -0
- package/dist/index.d.ts +2 -0
- package/dist/index.js +20 -0
- package/dist/index.js.map +1 -0
- package/package.json +45 -0
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 dev-goraebap
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/README.md
ADDED
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
# gentask
|
|
2
|
+
|
|
3
|
+
[gentask](https://gentask.xyz) 의 작업을 명령줄에서 다룹니다. 사람도 쓰고 에이전트도 씁니다.
|
|
4
|
+
|
|
5
|
+
```bash
|
|
6
|
+
npx gentask list
|
|
7
|
+
```
|
|
8
|
+
|
|
9
|
+
## 자격
|
|
10
|
+
|
|
11
|
+
계정 화면에서 에이전트 토큰을 발급한 뒤 저장합니다.
|
|
12
|
+
|
|
13
|
+
```bash
|
|
14
|
+
gentask auth login # 붙여 넣습니다
|
|
15
|
+
gentask auth login < t.txt # 파이프로도 받습니다
|
|
16
|
+
```
|
|
17
|
+
|
|
18
|
+
토큰은 `~/.config/gentask/config.json` 에 소유자만 읽을 수 있게 둡니다. `GENTASK_TOKEN` 으로 넘기면 저장된 것보다 그것이 먼저입니다.
|
|
19
|
+
|
|
20
|
+
**토큰을 인자로 받는 옵션은 없습니다.** 명령줄에 실린 자격은 셸 이력과 프로세스 목록에 남습니다.
|
|
21
|
+
|
|
22
|
+
## 명령
|
|
23
|
+
|
|
24
|
+
| 명령 | 하는 일 |
|
|
25
|
+
| :--- | :--- |
|
|
26
|
+
| `list [--all] [--json]` | 작업을 봅니다. 기본은 미완료만입니다 |
|
|
27
|
+
| `show <id> [--json]` | 하나를 펼칩니다 |
|
|
28
|
+
| `add <제목> [--due YYYY-MM-DD]` | 작업을 만듭니다. 만든 것의 식별자를 냅니다 |
|
|
29
|
+
| `edit <id> [--title] [--note] [--due] [--remind]` | 넘긴 것만 바꿉니다. 비우려면 빈 문자열을 넘깁니다 |
|
|
30
|
+
| `done <id> [--undo]` | 완료하거나 되돌립니다 |
|
|
31
|
+
| `star <id> [--off]` | 중요 표시를 켜거나 끕니다 |
|
|
32
|
+
| `today <id> [--off]` | 나의 하루에 담거나 뺍니다 |
|
|
33
|
+
| `rm <id>` | 지웁니다 |
|
|
34
|
+
| `auth login \| status \| logout` | 자격을 다룹니다 |
|
|
35
|
+
|
|
36
|
+
식별자는 앞 몇 자만 적어도 됩니다. 그것으로 하나가 가려지지 않으면 후보를 보여 주고 멈춥니다.
|
|
37
|
+
|
|
38
|
+
## 출구
|
|
39
|
+
|
|
40
|
+
사람이 읽는 것을 기본으로 내고 `--json` 이 기계가 읽는 것을 냅니다. 사람에게 하는 말은 표준오류로, 기계가 읽는 것은 표준출력으로 갑니다.
|
|
41
|
+
|
|
42
|
+
```bash
|
|
43
|
+
gentask list --json | jq '.[] | select(.important) | .title'
|
|
44
|
+
gentask add "장 보기" --due 2026-09-01 # 식별자만 낸다
|
|
45
|
+
```
|
|
46
|
+
|
|
47
|
+
## 에이전트에게
|
|
48
|
+
|
|
49
|
+
이 도구는 `Bash` 를 가진 에이전트가 그대로 부릅니다. 별도 설정이 필요 없습니다.
|
|
50
|
+
|
|
51
|
+
```
|
|
52
|
+
gentask --help 어떤 명령이 있는지
|
|
53
|
+
gentask list --json 지금 무엇이 있는지
|
|
54
|
+
```
|
|
55
|
+
|
|
56
|
+
## 다른 자리
|
|
57
|
+
|
|
58
|
+
`GENTASK_BASE_URL` 이 부를 자리를 바꿉니다. 로컬 서버를 볼 때 씁니다.
|
|
59
|
+
|
|
60
|
+
```bash
|
|
61
|
+
GENTASK_BASE_URL=http://localhost:8080 gentask list
|
|
62
|
+
```
|
|
63
|
+
|
|
64
|
+
## 규격
|
|
65
|
+
|
|
66
|
+
명령의 경계와 자격의 규약은 [결정-0013](https://github.com/dev-goraebap/gentask/blob/main/docs/architecture/decisions/0013-agent-cli.md)이, 흐름은 유스케이스 서술서 `AGT-001` 이 갖습니다.
|
package/dist/cli.d.ts
ADDED
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
import { GentaskClient } from './gentask-client.js';
|
|
2
|
+
/**
|
|
3
|
+
* 명령의 결과.
|
|
4
|
+
*
|
|
5
|
+
* <p>표준출력에 낼 것과 종료 코드를 갖는다. 여기서 직접 쓰지 않는 것은 시험이 출력을 읽기
|
|
6
|
+
* 위해서다 — 명령이 무엇을 냈는지 문자열로 받아 본다.
|
|
7
|
+
*/
|
|
8
|
+
export interface Outcome {
|
|
9
|
+
readonly out: string;
|
|
10
|
+
readonly code: number;
|
|
11
|
+
}
|
|
12
|
+
/** 클라이언트를 만드는 자리. 시험이 가짜를 끼운다. */
|
|
13
|
+
export type ClientFactory = () => GentaskClient;
|
|
14
|
+
/**
|
|
15
|
+
* 명령 하나를 실행한다.
|
|
16
|
+
*
|
|
17
|
+
* <p>실패는 던진다. 무엇을 어떻게 알릴지는 진입점이 정한다.
|
|
18
|
+
*/
|
|
19
|
+
export declare function run(argv: readonly string[], makeClient?: ClientFactory, env?: NodeJS.ProcessEnv, stdin?: () => Promise<string>): Promise<Outcome>;
|
package/dist/cli.js
ADDED
|
@@ -0,0 +1,225 @@
|
|
|
1
|
+
import { createInterface } from 'node:readline/promises';
|
|
2
|
+
import { parseArgs } from 'node:util';
|
|
3
|
+
import { clearToken, configPath, readConfig, readStoredToken, storeToken } from './config.js';
|
|
4
|
+
import { GentaskClient } from './gentask-client.js';
|
|
5
|
+
import { formatList, formatTask } from './format.js';
|
|
6
|
+
const HELP = `gentask — 작업을 명령줄에서 다룹니다
|
|
7
|
+
|
|
8
|
+
사용법
|
|
9
|
+
gentask <명령> [인자] [옵션]
|
|
10
|
+
|
|
11
|
+
작업
|
|
12
|
+
list [--all] [--json] 작업을 봅니다. 기본은 미완료만입니다
|
|
13
|
+
show <id> [--json] 하나를 펼칩니다
|
|
14
|
+
add <제목> [--due YYYY-MM-DD] 작업을 만듭니다
|
|
15
|
+
edit <id> [--title …] [--note …] [--due …] [--remind …]
|
|
16
|
+
넘긴 것만 바꿉니다. 비우려면 빈 문자열을 넘깁니다
|
|
17
|
+
done <id> [--undo] 완료하거나 되돌립니다
|
|
18
|
+
star <id> [--off] 중요 표시를 켜거나 끕니다
|
|
19
|
+
today <id> [--off] 나의 하루에 담거나 뺍니다
|
|
20
|
+
rm <id> 지웁니다
|
|
21
|
+
|
|
22
|
+
자격
|
|
23
|
+
auth login 토큰을 저장합니다. 표준입력으로 받으므로 이력에 남지 않습니다
|
|
24
|
+
auth status 어디서 온 토큰을 쓰는지 봅니다
|
|
25
|
+
auth logout 저장된 토큰을 지웁니다
|
|
26
|
+
|
|
27
|
+
식별자는 앞 몇 자만 적어도 됩니다. 그것으로 하나가 가려지지 않으면 그 사실을 알립니다.`;
|
|
28
|
+
/**
|
|
29
|
+
* 명령 하나를 실행한다.
|
|
30
|
+
*
|
|
31
|
+
* <p>실패는 던진다. 무엇을 어떻게 알릴지는 진입점이 정한다.
|
|
32
|
+
*/
|
|
33
|
+
export async function run(argv, makeClient = () => new GentaskClient(readConfig()), env = process.env, stdin = readStdin) {
|
|
34
|
+
const [command, ...rest] = argv;
|
|
35
|
+
if (command === undefined || command === '--help' || command === '-h' || command === 'help') {
|
|
36
|
+
return { out: HELP, code: 0 };
|
|
37
|
+
}
|
|
38
|
+
if (command === 'auth') {
|
|
39
|
+
return await runAuth(rest, env, stdin);
|
|
40
|
+
}
|
|
41
|
+
return await runTask(command, rest, makeClient);
|
|
42
|
+
}
|
|
43
|
+
// --- 자격 -----------------------------------------------------------------------------------------
|
|
44
|
+
async function runAuth(argv, env, stdin) {
|
|
45
|
+
const [sub, ...rest] = argv;
|
|
46
|
+
if (sub === 'login') {
|
|
47
|
+
parseArgs({ args: [...rest], allowPositionals: false });
|
|
48
|
+
const token = (await stdin()).trim();
|
|
49
|
+
if (!token) {
|
|
50
|
+
throw new Error('토큰이 비어 있습니다.');
|
|
51
|
+
}
|
|
52
|
+
const path = storeToken(token, env);
|
|
53
|
+
return { out: `토큰을 ${path} 에 저장했습니다.`, code: 0 };
|
|
54
|
+
}
|
|
55
|
+
if (sub === 'status') {
|
|
56
|
+
if (env['GENTASK_TOKEN']?.trim()) {
|
|
57
|
+
return { out: 'GENTASK_TOKEN 의 토큰을 씁니다. 저장된 것보다 이것이 먼저입니다.', code: 0 };
|
|
58
|
+
}
|
|
59
|
+
if (readStoredToken(env)) {
|
|
60
|
+
return { out: `${configPath(env)} 의 토큰을 씁니다.`, code: 0 };
|
|
61
|
+
}
|
|
62
|
+
return { out: '저장된 토큰이 없습니다.', code: 1 };
|
|
63
|
+
}
|
|
64
|
+
if (sub === 'logout') {
|
|
65
|
+
return clearToken(env)
|
|
66
|
+
? { out: '저장된 토큰을 지웠습니다.', code: 0 }
|
|
67
|
+
: { out: '지울 토큰이 없습니다.', code: 0 };
|
|
68
|
+
}
|
|
69
|
+
throw new Error(`auth 의 하위 명령이 아닙니다: ${sub ?? '(없음)'}`);
|
|
70
|
+
}
|
|
71
|
+
/**
|
|
72
|
+
* 토큰을 받는다.
|
|
73
|
+
*
|
|
74
|
+
* <p>터미널이면 한 줄만 받고, 파이프로 들어오면 끝까지 읽는다. 어느 쪽이든 표준입력이라 명령줄에
|
|
75
|
+
* 남지 않는다 — 결정-0013 이 토큰을 인자로 받지 않기로 한 자리다.
|
|
76
|
+
*/
|
|
77
|
+
async function readStdin() {
|
|
78
|
+
if (process.stdin.isTTY) {
|
|
79
|
+
process.stderr.write('토큰을 붙여 넣고 Enter 를 누르세요: ');
|
|
80
|
+
const rl = createInterface({ input: process.stdin });
|
|
81
|
+
try {
|
|
82
|
+
for await (const line of rl) {
|
|
83
|
+
return line;
|
|
84
|
+
}
|
|
85
|
+
return '';
|
|
86
|
+
}
|
|
87
|
+
finally {
|
|
88
|
+
rl.close();
|
|
89
|
+
process.stderr.write('\n');
|
|
90
|
+
}
|
|
91
|
+
}
|
|
92
|
+
const chunks = [];
|
|
93
|
+
for await (const chunk of process.stdin) {
|
|
94
|
+
chunks.push(Buffer.from(chunk));
|
|
95
|
+
}
|
|
96
|
+
return Buffer.concat(chunks).toString('utf8');
|
|
97
|
+
}
|
|
98
|
+
// --- 작업 -----------------------------------------------------------------------------------------
|
|
99
|
+
async function runTask(command, argv, makeClient) {
|
|
100
|
+
const client = makeClient();
|
|
101
|
+
switch (command) {
|
|
102
|
+
case 'list': {
|
|
103
|
+
const { values } = parseArgs({
|
|
104
|
+
args: [...argv],
|
|
105
|
+
options: { all: { type: 'boolean' }, json: { type: 'boolean' } },
|
|
106
|
+
allowPositionals: false,
|
|
107
|
+
});
|
|
108
|
+
const tasks = await client.list();
|
|
109
|
+
const shown = values.all ? tasks : tasks.filter((t) => !t.completedAt);
|
|
110
|
+
return values.json
|
|
111
|
+
? { out: JSON.stringify(shown, null, 2), code: 0 }
|
|
112
|
+
: { out: formatList(shown), code: 0 };
|
|
113
|
+
}
|
|
114
|
+
case 'show': {
|
|
115
|
+
const { values, positionals } = parseArgs({
|
|
116
|
+
args: [...argv],
|
|
117
|
+
options: { json: { type: 'boolean' } },
|
|
118
|
+
allowPositionals: true,
|
|
119
|
+
});
|
|
120
|
+
const task = await resolve(client, positionals[0]);
|
|
121
|
+
return values.json
|
|
122
|
+
? { out: JSON.stringify(task, null, 2), code: 0 }
|
|
123
|
+
: { out: formatTask(task), code: 0 };
|
|
124
|
+
}
|
|
125
|
+
case 'add': {
|
|
126
|
+
const { values, positionals } = parseArgs({
|
|
127
|
+
args: [...argv],
|
|
128
|
+
options: { due: { type: 'string' } },
|
|
129
|
+
allowPositionals: true,
|
|
130
|
+
});
|
|
131
|
+
const title = positionals.join(' ').trim();
|
|
132
|
+
if (!title) {
|
|
133
|
+
throw new Error('제목이 필요합니다. gentask add "할 일"');
|
|
134
|
+
}
|
|
135
|
+
const id = await client.add(title, values.due ?? null);
|
|
136
|
+
return { out: id, code: 0 };
|
|
137
|
+
}
|
|
138
|
+
case 'edit': {
|
|
139
|
+
const { values, positionals } = parseArgs({
|
|
140
|
+
args: [...argv],
|
|
141
|
+
options: {
|
|
142
|
+
title: { type: 'string' },
|
|
143
|
+
note: { type: 'string' },
|
|
144
|
+
due: { type: 'string' },
|
|
145
|
+
remind: { type: 'string' },
|
|
146
|
+
},
|
|
147
|
+
allowPositionals: true,
|
|
148
|
+
});
|
|
149
|
+
const task = await resolve(client, positionals[0]);
|
|
150
|
+
// 서버의 편집은 부분 갱신이 아니라 넷을 그대로 받는다. 넘기지 않은 것은 지금 값을 다시 보낸다.
|
|
151
|
+
await client.edit(task.id, {
|
|
152
|
+
title: values.title ?? task.title,
|
|
153
|
+
note: values.note ?? task.note ?? '',
|
|
154
|
+
dueDate: values.due === undefined ? (task.dueDate ?? null) : blankToNull(values.due),
|
|
155
|
+
remindAt: values.remind === undefined ? (task.remindAt ?? null) : blankToNull(values.remind),
|
|
156
|
+
});
|
|
157
|
+
return { out: task.id, code: 0 };
|
|
158
|
+
}
|
|
159
|
+
case 'done': {
|
|
160
|
+
const { values, positionals } = parseArgs({
|
|
161
|
+
args: [...argv],
|
|
162
|
+
options: { undo: { type: 'boolean' } },
|
|
163
|
+
allowPositionals: true,
|
|
164
|
+
});
|
|
165
|
+
const task = await resolve(client, positionals[0]);
|
|
166
|
+
await client.setCompleted(task.id, !values.undo);
|
|
167
|
+
return { out: task.id, code: 0 };
|
|
168
|
+
}
|
|
169
|
+
case 'star': {
|
|
170
|
+
const { values, positionals } = parseArgs({
|
|
171
|
+
args: [...argv],
|
|
172
|
+
options: { off: { type: 'boolean' } },
|
|
173
|
+
allowPositionals: true,
|
|
174
|
+
});
|
|
175
|
+
const task = await resolve(client, positionals[0]);
|
|
176
|
+
await client.setImportant(task.id, !values.off);
|
|
177
|
+
return { out: task.id, code: 0 };
|
|
178
|
+
}
|
|
179
|
+
case 'today': {
|
|
180
|
+
const { values, positionals } = parseArgs({
|
|
181
|
+
args: [...argv],
|
|
182
|
+
options: { off: { type: 'boolean' } },
|
|
183
|
+
allowPositionals: true,
|
|
184
|
+
});
|
|
185
|
+
const task = await resolve(client, positionals[0]);
|
|
186
|
+
await client.setMyDay(task.id, !values.off);
|
|
187
|
+
return { out: task.id, code: 0 };
|
|
188
|
+
}
|
|
189
|
+
case 'rm': {
|
|
190
|
+
const { positionals } = parseArgs({ args: [...argv], allowPositionals: true });
|
|
191
|
+
const task = await resolve(client, positionals[0]);
|
|
192
|
+
await client.remove(task.id);
|
|
193
|
+
return { out: task.id, code: 0 };
|
|
194
|
+
}
|
|
195
|
+
default:
|
|
196
|
+
throw new Error(`명령이 아닙니다: ${command}\n\ngentask --help 로 목록을 봅니다.`);
|
|
197
|
+
}
|
|
198
|
+
}
|
|
199
|
+
function blankToNull(value) {
|
|
200
|
+
return value.trim() === '' ? null : value;
|
|
201
|
+
}
|
|
202
|
+
/**
|
|
203
|
+
* 적어 넣은 것으로 작업 하나를 가린다.
|
|
204
|
+
*
|
|
205
|
+
* <p>전문이면 그대로 부르고, 짧게 적었으면 목록에서 앞이 맞는 것을 찾는다. 둘 이상이면 고르지
|
|
206
|
+
* 않고 그 사실을 알린다 — 아무거나 골라 지우면 되돌릴 수 없다.
|
|
207
|
+
*/
|
|
208
|
+
async function resolve(client, given) {
|
|
209
|
+
const needle = given?.trim();
|
|
210
|
+
if (!needle) {
|
|
211
|
+
throw new Error('작업의 식별자가 필요합니다.');
|
|
212
|
+
}
|
|
213
|
+
if (needle.length === 36) {
|
|
214
|
+
return await client.get(needle);
|
|
215
|
+
}
|
|
216
|
+
const matched = (await client.list()).filter((t) => t.id.startsWith(needle));
|
|
217
|
+
if (matched.length === 1) {
|
|
218
|
+
return matched[0];
|
|
219
|
+
}
|
|
220
|
+
if (matched.length === 0) {
|
|
221
|
+
throw new Error(`그 식별자로 시작하는 작업이 없습니다: ${needle}`);
|
|
222
|
+
}
|
|
223
|
+
throw new Error([`${needle} 로 시작하는 작업이 ${matched.length} 개입니다. 더 적어 주세요.`, '', formatList(matched)].join('\n'));
|
|
224
|
+
}
|
|
225
|
+
//# sourceMappingURL=cli.js.map
|
package/dist/cli.js.map
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"cli.js","sourceRoot":"","sources":["../src/cli.ts"],"names":[],"mappings":"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"}
|
package/dist/config.d.ts
ADDED
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* 붙을 자리와 자격.
|
|
3
|
+
*
|
|
4
|
+
* <p>규격은 결정-0013 이 갖는다.
|
|
5
|
+
*/
|
|
6
|
+
export interface Config {
|
|
7
|
+
readonly baseUrl: string;
|
|
8
|
+
readonly token: string;
|
|
9
|
+
}
|
|
10
|
+
export declare const DEFAULT_BASE_URL = "https://api.gentask.xyz";
|
|
11
|
+
/** 토큰이 없을 때 사용자에게 보일 말. 무엇을 어디서 해야 하는지까지 담는다. */
|
|
12
|
+
export declare const MISSING_TOKEN: string;
|
|
13
|
+
/**
|
|
14
|
+
* 저장된 자격이 사는 자리.
|
|
15
|
+
*
|
|
16
|
+
* <p>XDG 를 따르되 그 변수가 없으면 홈 아래 `.config` 로 간다. 리눅스와 맥은 이것이 관례이고,
|
|
17
|
+
* 윈도우에는 XDG 가 없으나 홈 아래에 나는 것이 사용자가 찾기 쉽다.
|
|
18
|
+
*/
|
|
19
|
+
export declare function configPath(env?: NodeJS.ProcessEnv): string;
|
|
20
|
+
/** 저장된 토큰. 파일이 없거나 읽을 수 없으면 없는 것으로 본다. */
|
|
21
|
+
export declare function readStoredToken(env?: NodeJS.ProcessEnv): string | null;
|
|
22
|
+
/**
|
|
23
|
+
* 토큰을 저장한다.
|
|
24
|
+
*
|
|
25
|
+
* <p>소유자만 읽을 수 있게 둔다. 같은 기계의 다른 사용자가 읽을 수 있으면 그 계정의 전권이 함께
|
|
26
|
+
* 넘어간다. TG-011 의 #8 이 이것이다.
|
|
27
|
+
*/
|
|
28
|
+
export declare function storeToken(token: string, env?: NodeJS.ProcessEnv): string;
|
|
29
|
+
/** 저장된 자격을 지운다. 없었으면 false 를 낸다. */
|
|
30
|
+
export declare function clearToken(env?: NodeJS.ProcessEnv): boolean;
|
|
31
|
+
/**
|
|
32
|
+
* 자격을 찾는다. 환경이 파일을 이긴다.
|
|
33
|
+
*
|
|
34
|
+
* <p>환경변수는 그 프로세스 하나에만 걸리므로, 저장해 둔 것을 건드리지 않고 다른 계정이나 다른
|
|
35
|
+
* 서버를 한 번 볼 수 있다. TG-011 의 #9 가 이것이다.
|
|
36
|
+
*/
|
|
37
|
+
export declare function readConfig(env?: NodeJS.ProcessEnv): Config;
|
package/dist/config.js
ADDED
|
@@ -0,0 +1,72 @@
|
|
|
1
|
+
import { chmodSync, mkdirSync, readFileSync, rmSync, writeFileSync } from 'node:fs';
|
|
2
|
+
import { homedir } from 'node:os';
|
|
3
|
+
import { dirname, join } from 'node:path';
|
|
4
|
+
export const DEFAULT_BASE_URL = 'https://api.gentask.xyz';
|
|
5
|
+
/** 토큰이 없을 때 사용자에게 보일 말. 무엇을 어디서 해야 하는지까지 담는다. */
|
|
6
|
+
export const MISSING_TOKEN = [
|
|
7
|
+
'토큰이 없습니다. gentask.xyz 의 계정 화면에서 에이전트 토큰을 발급한 뒤',
|
|
8
|
+
'',
|
|
9
|
+
' gentask auth login',
|
|
10
|
+
'',
|
|
11
|
+
'을 실행하세요. GENTASK_TOKEN 으로 넘겨도 됩니다.',
|
|
12
|
+
].join('\n');
|
|
13
|
+
/**
|
|
14
|
+
* 저장된 자격이 사는 자리.
|
|
15
|
+
*
|
|
16
|
+
* <p>XDG 를 따르되 그 변수가 없으면 홈 아래 `.config` 로 간다. 리눅스와 맥은 이것이 관례이고,
|
|
17
|
+
* 윈도우에는 XDG 가 없으나 홈 아래에 나는 것이 사용자가 찾기 쉽다.
|
|
18
|
+
*/
|
|
19
|
+
export function configPath(env = process.env) {
|
|
20
|
+
const base = env['XDG_CONFIG_HOME']?.trim() || join(homedir(), '.config');
|
|
21
|
+
return join(base, 'gentask', 'config.json');
|
|
22
|
+
}
|
|
23
|
+
/** 저장된 토큰. 파일이 없거나 읽을 수 없으면 없는 것으로 본다. */
|
|
24
|
+
export function readStoredToken(env = process.env) {
|
|
25
|
+
try {
|
|
26
|
+
const raw = readFileSync(configPath(env), 'utf8');
|
|
27
|
+
const stored = JSON.parse(raw);
|
|
28
|
+
return stored.token?.trim() || null;
|
|
29
|
+
}
|
|
30
|
+
catch {
|
|
31
|
+
return null;
|
|
32
|
+
}
|
|
33
|
+
}
|
|
34
|
+
/**
|
|
35
|
+
* 토큰을 저장한다.
|
|
36
|
+
*
|
|
37
|
+
* <p>소유자만 읽을 수 있게 둔다. 같은 기계의 다른 사용자가 읽을 수 있으면 그 계정의 전권이 함께
|
|
38
|
+
* 넘어간다. TG-011 의 #8 이 이것이다.
|
|
39
|
+
*/
|
|
40
|
+
export function storeToken(token, env = process.env) {
|
|
41
|
+
const path = configPath(env);
|
|
42
|
+
mkdirSync(dirname(path), { recursive: true, mode: 0o700 });
|
|
43
|
+
writeFileSync(path, `${JSON.stringify({ token }, null, 2)}\n`, { mode: 0o600 });
|
|
44
|
+
chmodSync(path, 0o600);
|
|
45
|
+
return path;
|
|
46
|
+
}
|
|
47
|
+
/** 저장된 자격을 지운다. 없었으면 false 를 낸다. */
|
|
48
|
+
export function clearToken(env = process.env) {
|
|
49
|
+
const path = configPath(env);
|
|
50
|
+
if (readStoredToken(env) === null) {
|
|
51
|
+
return false;
|
|
52
|
+
}
|
|
53
|
+
rmSync(path, { force: true });
|
|
54
|
+
return true;
|
|
55
|
+
}
|
|
56
|
+
/**
|
|
57
|
+
* 자격을 찾는다. 환경이 파일을 이긴다.
|
|
58
|
+
*
|
|
59
|
+
* <p>환경변수는 그 프로세스 하나에만 걸리므로, 저장해 둔 것을 건드리지 않고 다른 계정이나 다른
|
|
60
|
+
* 서버를 한 번 볼 수 있다. TG-011 의 #9 가 이것이다.
|
|
61
|
+
*/
|
|
62
|
+
export function readConfig(env = process.env) {
|
|
63
|
+
const token = env['GENTASK_TOKEN']?.trim() || readStoredToken(env);
|
|
64
|
+
if (!token) {
|
|
65
|
+
throw new Error(MISSING_TOKEN);
|
|
66
|
+
}
|
|
67
|
+
return {
|
|
68
|
+
baseUrl: (env['GENTASK_BASE_URL']?.trim() || DEFAULT_BASE_URL).replace(/\/+$/, ''),
|
|
69
|
+
token,
|
|
70
|
+
};
|
|
71
|
+
}
|
|
72
|
+
//# sourceMappingURL=config.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"config.js","sourceRoot":"","sources":["../src/config.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,SAAS,EAAE,SAAS,EAAE,YAAY,EAAE,MAAM,EAAE,aAAa,EAAE,MAAM,SAAS,CAAC;AACpF,OAAO,EAAE,OAAO,EAAE,MAAM,SAAS,CAAC;AAClC,OAAO,EAAE,OAAO,EAAE,IAAI,EAAE,MAAM,WAAW,CAAC;AAY1C,MAAM,CAAC,MAAM,gBAAgB,GAAG,yBAAyB,CAAC;AAE1D,iDAAiD;AACjD,MAAM,CAAC,MAAM,aAAa,GAAG;IAC3B,gDAAgD;IAChD,EAAE;IACF,sBAAsB;IACtB,EAAE;IACF,oCAAoC;CACrC,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;AAEb;;;;;GAKG;AACH,MAAM,UAAU,UAAU,CAAC,MAAyB,OAAO,CAAC,GAAG;IAC7D,MAAM,IAAI,GAAG,GAAG,CAAC,iBAAiB,CAAC,EAAE,IAAI,EAAE,IAAI,IAAI,CAAC,OAAO,EAAE,EAAE,SAAS,CAAC,CAAC;IAC1E,OAAO,IAAI,CAAC,IAAI,EAAE,SAAS,EAAE,aAAa,CAAC,CAAC;AAC9C,CAAC;AAMD,0CAA0C;AAC1C,MAAM,UAAU,eAAe,CAAC,MAAyB,OAAO,CAAC,GAAG;IAClE,IAAI,CAAC;QACH,MAAM,GAAG,GAAG,YAAY,CAAC,UAAU,CAAC,GAAG,CAAC,EAAE,MAAM,CAAC,CAAC;QAClD,MAAM,MAAM,GAAG,IAAI,CAAC,KAAK,CAAC,GAAG,CAAiB,CAAC;QAC/C,OAAO,MAAM,CAAC,KAAK,EAAE,IAAI,EAAE,IAAI,IAAI,CAAC;IACtC,CAAC;IAAC,MAAM,CAAC;QACP,OAAO,IAAI,CAAC;IACd,CAAC;AACH,CAAC;AAED;;;;;GAKG;AACH,MAAM,UAAU,UAAU,CAAC,KAAa,EAAE,MAAyB,OAAO,CAAC,GAAG;IAC5E,MAAM,IAAI,GAAG,UAAU,CAAC,GAAG,CAAC,CAAC;IAC7B,SAAS,CAAC,OAAO,CAAC,IAAI,CAAC,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,IAAI,EAAE,KAAK,EAAE,CAAC,CAAC;IAC3D,aAAa,CAAC,IAAI,EAAE,GAAG,IAAI,CAAC,SAAS,CAAC,EAAE,KAAK,EAAE,EAAE,IAAI,EAAE,CAAC,CAAC,IAAI,EAAE,EAAE,IAAI,EAAE,KAAK,EAAE,CAAC,CAAC;IAChF,SAAS,CAAC,IAAI,EAAE,KAAK,CAAC,CAAC;IACvB,OAAO,IAAI,CAAC;AACd,CAAC;AAED,oCAAoC;AACpC,MAAM,UAAU,UAAU,CAAC,MAAyB,OAAO,CAAC,GAAG;IAC7D,MAAM,IAAI,GAAG,UAAU,CAAC,GAAG,CAAC,CAAC;IAC7B,IAAI,eAAe,CAAC,GAAG,CAAC,KAAK,IAAI,EAAE,CAAC;QAClC,OAAO,KAAK,CAAC;IACf,CAAC;IACD,MAAM,CAAC,IAAI,EAAE,EAAE,KAAK,EAAE,IAAI,EAAE,CAAC,CAAC;IAC9B,OAAO,IAAI,CAAC;AACd,CAAC;AAED;;;;;GAKG;AACH,MAAM,UAAU,UAAU,CAAC,MAAyB,OAAO,CAAC,GAAG;IAC7D,MAAM,KAAK,GAAG,GAAG,CAAC,eAAe,CAAC,EAAE,IAAI,EAAE,IAAI,eAAe,CAAC,GAAG,CAAC,CAAC;IACnE,IAAI,CAAC,KAAK,EAAE,CAAC;QACX,MAAM,IAAI,KAAK,CAAC,aAAa,CAAC,CAAC;IACjC,CAAC;IACD,OAAO;QACL,OAAO,EAAE,CAAC,GAAG,CAAC,kBAAkB,CAAC,EAAE,IAAI,EAAE,IAAI,gBAAgB,CAAC,CAAC,OAAO,CAAC,MAAM,EAAE,EAAE,CAAC;QAClF,KAAK;KACN,CAAC;AACJ,CAAC"}
|
package/dist/format.d.ts
ADDED
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
import type { Task } from './gentask-client.js';
|
|
2
|
+
/**
|
|
3
|
+
* 사람이 읽는 출구.
|
|
4
|
+
*
|
|
5
|
+
* <p>기계가 읽는 것은 `--json` 이 낸다. 둘을 한 명령이 갖되 섞지 않는 것은 결정-0013 의 규격이다.
|
|
6
|
+
*/
|
|
7
|
+
/** 식별자는 앞 여덟 자만 보인다. 목록에서 고르는 데는 그것으로 충분하고 전문은 줄을 다 먹는다. */
|
|
8
|
+
export declare function shortId(id: string): string;
|
|
9
|
+
/**
|
|
10
|
+
* 화면에서 차지하는 칸 수.
|
|
11
|
+
*
|
|
12
|
+
* <p>한글과 한자와 가나는 한 글자가 두 칸이다. 글자 수로 세면 표의 세로줄이 어긋난다. 범위는
|
|
13
|
+
* 유니코드의 East Asian Width 가 W 와 F 로 정한 자리를 따른다.
|
|
14
|
+
*/
|
|
15
|
+
export declare function displayWidth(text: string): number;
|
|
16
|
+
/** 목록을 표로 그린다. 비었으면 그 사실을 한 줄로 말한다. */
|
|
17
|
+
export declare function formatList(tasks: readonly Task[]): string;
|
|
18
|
+
/** 하나를 펼친다. 값이 없는 줄은 내지 않는다. */
|
|
19
|
+
export declare function formatTask(task: Task): string;
|
package/dist/format.js
ADDED
|
@@ -0,0 +1,95 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* 사람이 읽는 출구.
|
|
3
|
+
*
|
|
4
|
+
* <p>기계가 읽는 것은 `--json` 이 낸다. 둘을 한 명령이 갖되 섞지 않는 것은 결정-0013 의 규격이다.
|
|
5
|
+
*/
|
|
6
|
+
/** 식별자는 앞 여덟 자만 보인다. 목록에서 고르는 데는 그것으로 충분하고 전문은 줄을 다 먹는다. */
|
|
7
|
+
export function shortId(id) {
|
|
8
|
+
return id.slice(0, 8);
|
|
9
|
+
}
|
|
10
|
+
/** 완료 · 중요 · 나의 하루를 한 칸에 담는다. 켜진 것만 글자를 갖는다. */
|
|
11
|
+
function marks(task) {
|
|
12
|
+
return [
|
|
13
|
+
task.completedAt ? 'x' : ' ',
|
|
14
|
+
task.important ? '*' : ' ',
|
|
15
|
+
task.myDayOn ? 'o' : ' ',
|
|
16
|
+
].join('');
|
|
17
|
+
}
|
|
18
|
+
/** 기한과 미리 알림. 둘 다 없으면 빈 칸이다. */
|
|
19
|
+
function when(task) {
|
|
20
|
+
const parts = [];
|
|
21
|
+
if (task.dueDate) {
|
|
22
|
+
parts.push(task.dueDate);
|
|
23
|
+
}
|
|
24
|
+
if (task.remindAt) {
|
|
25
|
+
parts.push(task.remindAt.replace('T', ' ').slice(0, 16));
|
|
26
|
+
}
|
|
27
|
+
return parts.join(' · ');
|
|
28
|
+
}
|
|
29
|
+
/**
|
|
30
|
+
* 화면에서 차지하는 칸 수.
|
|
31
|
+
*
|
|
32
|
+
* <p>한글과 한자와 가나는 한 글자가 두 칸이다. 글자 수로 세면 표의 세로줄이 어긋난다. 범위는
|
|
33
|
+
* 유니코드의 East Asian Width 가 W 와 F 로 정한 자리를 따른다.
|
|
34
|
+
*/
|
|
35
|
+
export function displayWidth(text) {
|
|
36
|
+
let width = 0;
|
|
37
|
+
for (const ch of text) {
|
|
38
|
+
const c = ch.codePointAt(0) ?? 0;
|
|
39
|
+
const wide = (c >= 0x1100 && c <= 0x115f) ||
|
|
40
|
+
(c >= 0x2e80 && c <= 0x303e) ||
|
|
41
|
+
(c >= 0x3041 && c <= 0x33ff) ||
|
|
42
|
+
(c >= 0x3400 && c <= 0x4dbf) ||
|
|
43
|
+
(c >= 0x4e00 && c <= 0x9fff) ||
|
|
44
|
+
(c >= 0xa000 && c <= 0xa4cf) ||
|
|
45
|
+
(c >= 0xac00 && c <= 0xd7a3) ||
|
|
46
|
+
(c >= 0xf900 && c <= 0xfaff) ||
|
|
47
|
+
(c >= 0xfe30 && c <= 0xfe6f) ||
|
|
48
|
+
(c >= 0xff00 && c <= 0xff60) ||
|
|
49
|
+
(c >= 0xffe0 && c <= 0xffe6) ||
|
|
50
|
+
(c >= 0x20000 && c <= 0x3fffd);
|
|
51
|
+
width += wide ? 2 : 1;
|
|
52
|
+
}
|
|
53
|
+
return width;
|
|
54
|
+
}
|
|
55
|
+
function padTo(text, target) {
|
|
56
|
+
return text + ' '.repeat(Math.max(0, target - displayWidth(text)));
|
|
57
|
+
}
|
|
58
|
+
/** 목록을 표로 그린다. 비었으면 그 사실을 한 줄로 말한다. */
|
|
59
|
+
export function formatList(tasks) {
|
|
60
|
+
if (tasks.length === 0) {
|
|
61
|
+
return '작업이 없습니다.';
|
|
62
|
+
}
|
|
63
|
+
const rows = tasks.map((task) => ({
|
|
64
|
+
id: shortId(task.id),
|
|
65
|
+
marks: marks(task),
|
|
66
|
+
title: task.title,
|
|
67
|
+
when: when(task),
|
|
68
|
+
}));
|
|
69
|
+
const titleWidth = Math.max(...rows.map((r) => displayWidth(r.title)), 5);
|
|
70
|
+
return rows
|
|
71
|
+
.map((r) => `${r.id} ${r.marks} ${padTo(r.title, titleWidth)} ${r.when}`.trimEnd())
|
|
72
|
+
.join('\n');
|
|
73
|
+
}
|
|
74
|
+
/** 하나를 펼친다. 값이 없는 줄은 내지 않는다. */
|
|
75
|
+
export function formatTask(task) {
|
|
76
|
+
const lines = [`${task.title}`, ` 식별자 ${task.id}`];
|
|
77
|
+
if (task.note) {
|
|
78
|
+
lines.push(` 메모 ${task.note.replace(/\n/g, '\n ')}`);
|
|
79
|
+
}
|
|
80
|
+
if (task.dueDate) {
|
|
81
|
+
lines.push(` 기한 ${task.dueDate}`);
|
|
82
|
+
}
|
|
83
|
+
if (task.remindAt) {
|
|
84
|
+
lines.push(` 미리알림 ${task.remindAt.replace('T', ' ').slice(0, 16)}`);
|
|
85
|
+
}
|
|
86
|
+
if (task.myDayOn) {
|
|
87
|
+
lines.push(` 나의하루 ${task.myDayOn}`);
|
|
88
|
+
}
|
|
89
|
+
if (task.important) {
|
|
90
|
+
lines.push(' 중요 예');
|
|
91
|
+
}
|
|
92
|
+
lines.push(` 상태 ${task.completedAt ? '완료' : '미완료'}`);
|
|
93
|
+
return lines.join('\n');
|
|
94
|
+
}
|
|
95
|
+
//# sourceMappingURL=format.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"format.js","sourceRoot":"","sources":["../src/format.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
import type { Config } from './config.js';
|
|
2
|
+
import type { components } from 'api-types';
|
|
3
|
+
/**
|
|
4
|
+
* 작업 하나.
|
|
5
|
+
*
|
|
6
|
+
* <p>손으로 적지 않고 API 명세가 만든 것을 쓴다(결정-0003). 계약이 바뀌면 이 자리가 컴파일 오류로
|
|
7
|
+
* 드러나며, 다시 적어 두면 그 변화가 조용히 지나간다.
|
|
8
|
+
*/
|
|
9
|
+
export type Task = components['schemas']['TaskView'];
|
|
10
|
+
/**
|
|
11
|
+
* 서버가 거절했다.
|
|
12
|
+
*
|
|
13
|
+
* <p>사유를 그대로 옮긴다. 다시 시도할지 사용자에게 물을지는 이 서버가 정하지 않고 에이전트가 정한다.
|
|
14
|
+
*/
|
|
15
|
+
export declare class GentaskError extends Error {
|
|
16
|
+
readonly status: number;
|
|
17
|
+
readonly code: string | null;
|
|
18
|
+
constructor(status: number, code: string | null, detail: string);
|
|
19
|
+
}
|
|
20
|
+
/** 자격이 서지 않았다. 토큰을 다시 발급해야 하는 자리이며 AGT-001 의 A2 다. */
|
|
21
|
+
export declare const UNAUTHENTICATED = "\uD1A0\uD070\uC774 \uBC1B\uC544\uB4E4\uC5EC\uC9C0\uC9C0 \uC54A\uC558\uC2B5\uB2C8\uB2E4. gentask \uC758 \uACC4\uC815 \uD654\uBA74\uC5D0\uC11C \uC5D0\uC774\uC804\uD2B8 \uD1A0\uD070\uC744 \uB2E4\uC2DC \uBC1C\uAE09\uD574 \uC124\uC815\uC5D0 \uB450\uC138\uC694.";
|
|
22
|
+
/**
|
|
23
|
+
* 작업 API 를 부르는 자리.
|
|
24
|
+
*
|
|
25
|
+
* <p>판정하거나 저장하는 것이 없다. 규칙은 모두 서버가 가지며 여기는 옮기기만 한다.
|
|
26
|
+
*/
|
|
27
|
+
export declare class GentaskClient {
|
|
28
|
+
private readonly config;
|
|
29
|
+
private readonly fetchFn;
|
|
30
|
+
constructor(config: Config, fetchFn?: typeof fetch);
|
|
31
|
+
list(): Promise<readonly Task[]>;
|
|
32
|
+
get(taskId: string): Promise<Task>;
|
|
33
|
+
/** 만든 작업의 식별자는 Location 헤더가 낸다. */
|
|
34
|
+
add(title: string, dueDate: string | null): Promise<string>;
|
|
35
|
+
/** 네 값을 함께 보낸다. 서버의 편집은 부분 갱신이 아니라 그 넷을 그대로 받는다. */
|
|
36
|
+
edit(taskId: string, fields: {
|
|
37
|
+
title: string;
|
|
38
|
+
note: string;
|
|
39
|
+
dueDate: string | null;
|
|
40
|
+
remindAt: string | null;
|
|
41
|
+
}): Promise<void>;
|
|
42
|
+
setCompleted(taskId: string, completed: boolean): Promise<void>;
|
|
43
|
+
setImportant(taskId: string, important: boolean): Promise<void>;
|
|
44
|
+
setMyDay(taskId: string, inMyDay: boolean): Promise<void>;
|
|
45
|
+
remove(taskId: string): Promise<void>;
|
|
46
|
+
private send;
|
|
47
|
+
private raw;
|
|
48
|
+
}
|
|
@@ -0,0 +1,104 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* 서버가 거절했다.
|
|
3
|
+
*
|
|
4
|
+
* <p>사유를 그대로 옮긴다. 다시 시도할지 사용자에게 물을지는 이 서버가 정하지 않고 에이전트가 정한다.
|
|
5
|
+
*/
|
|
6
|
+
export class GentaskError extends Error {
|
|
7
|
+
status;
|
|
8
|
+
code;
|
|
9
|
+
constructor(status, code, detail) {
|
|
10
|
+
super(detail);
|
|
11
|
+
this.status = status;
|
|
12
|
+
this.code = code;
|
|
13
|
+
this.name = 'GentaskError';
|
|
14
|
+
}
|
|
15
|
+
}
|
|
16
|
+
/** 자격이 서지 않았다. 토큰을 다시 발급해야 하는 자리이며 AGT-001 의 A2 다. */
|
|
17
|
+
export const UNAUTHENTICATED = '토큰이 받아들여지지 않았습니다. gentask 의 계정 화면에서 에이전트 토큰을 다시 발급해 설정에 두세요.';
|
|
18
|
+
/**
|
|
19
|
+
* 작업 API 를 부르는 자리.
|
|
20
|
+
*
|
|
21
|
+
* <p>판정하거나 저장하는 것이 없다. 규칙은 모두 서버가 가지며 여기는 옮기기만 한다.
|
|
22
|
+
*/
|
|
23
|
+
export class GentaskClient {
|
|
24
|
+
config;
|
|
25
|
+
fetchFn;
|
|
26
|
+
constructor(config, fetchFn = fetch) {
|
|
27
|
+
this.config = config;
|
|
28
|
+
this.fetchFn = fetchFn;
|
|
29
|
+
}
|
|
30
|
+
list() {
|
|
31
|
+
return this.send('GET', '/api/v1/tasks');
|
|
32
|
+
}
|
|
33
|
+
get(taskId) {
|
|
34
|
+
return this.send('GET', `/api/v1/tasks/${encodeURIComponent(taskId)}`);
|
|
35
|
+
}
|
|
36
|
+
/** 만든 작업의 식별자는 Location 헤더가 낸다. */
|
|
37
|
+
async add(title, dueDate) {
|
|
38
|
+
const response = await this.raw('POST', '/api/v1/tasks', { title, dueDate });
|
|
39
|
+
const location = response.headers.get('location') ?? '';
|
|
40
|
+
return location.split('/').pop() ?? '';
|
|
41
|
+
}
|
|
42
|
+
/** 네 값을 함께 보낸다. 서버의 편집은 부분 갱신이 아니라 그 넷을 그대로 받는다. */
|
|
43
|
+
async edit(taskId, fields) {
|
|
44
|
+
await this.raw('PATCH', `/api/v1/tasks/${encodeURIComponent(taskId)}`, fields);
|
|
45
|
+
}
|
|
46
|
+
async setCompleted(taskId, completed) {
|
|
47
|
+
await this.raw('PATCH', `/api/v1/tasks/${encodeURIComponent(taskId)}/completion`, {
|
|
48
|
+
completed,
|
|
49
|
+
});
|
|
50
|
+
}
|
|
51
|
+
async setImportant(taskId, important) {
|
|
52
|
+
await this.raw('PATCH', `/api/v1/tasks/${encodeURIComponent(taskId)}/importance`, {
|
|
53
|
+
important,
|
|
54
|
+
});
|
|
55
|
+
}
|
|
56
|
+
async setMyDay(taskId, inMyDay) {
|
|
57
|
+
await this.raw('PATCH', `/api/v1/tasks/${encodeURIComponent(taskId)}/my-day`, { inMyDay });
|
|
58
|
+
}
|
|
59
|
+
async remove(taskId) {
|
|
60
|
+
await this.raw('DELETE', `/api/v1/tasks/${encodeURIComponent(taskId)}`);
|
|
61
|
+
}
|
|
62
|
+
// --- 보조 ---------------------------------------------------------------------------------------
|
|
63
|
+
async send(method, path) {
|
|
64
|
+
const response = await this.raw(method, path);
|
|
65
|
+
return (await response.json());
|
|
66
|
+
}
|
|
67
|
+
async raw(method, path, body) {
|
|
68
|
+
const response = await this.fetchFn(`${this.config.baseUrl}${path}`, {
|
|
69
|
+
method,
|
|
70
|
+
headers: {
|
|
71
|
+
Authorization: `Bearer ${this.config.token}`,
|
|
72
|
+
...(body === undefined ? {} : { 'Content-Type': 'application/json' }),
|
|
73
|
+
},
|
|
74
|
+
...(body === undefined ? {} : { body: JSON.stringify(body) }),
|
|
75
|
+
});
|
|
76
|
+
if (!response.ok) {
|
|
77
|
+
throw await toError(response);
|
|
78
|
+
}
|
|
79
|
+
return response;
|
|
80
|
+
}
|
|
81
|
+
}
|
|
82
|
+
/**
|
|
83
|
+
* 실패 응답을 옮긴다.
|
|
84
|
+
*
|
|
85
|
+
* <p>서버는 RFC 9457 로 답하며 `code` 가 분기의 계약이다(결정-0004). 자격이 서지 않은 것만 여기서
|
|
86
|
+
* 말을 바꾸는데, 그 자리는 토큰을 다시 발급해야 한다는 것이 사용자가 알아야 할 전부이기 때문이다.
|
|
87
|
+
*/
|
|
88
|
+
async function toError(response) {
|
|
89
|
+
if (response.status === 401) {
|
|
90
|
+
return new GentaskError(401, 'UNAUTHENTICATED', UNAUTHENTICATED);
|
|
91
|
+
}
|
|
92
|
+
let code = null;
|
|
93
|
+
let detail = `요청이 실패했습니다 (HTTP ${response.status})`;
|
|
94
|
+
try {
|
|
95
|
+
const problem = (await response.json());
|
|
96
|
+
code = problem.code ?? null;
|
|
97
|
+
detail = problem.detail ?? detail;
|
|
98
|
+
}
|
|
99
|
+
catch {
|
|
100
|
+
// 본문이 없거나 JSON 이 아니면 상태 코드만 남긴다
|
|
101
|
+
}
|
|
102
|
+
return new GentaskError(response.status, code, detail);
|
|
103
|
+
}
|
|
104
|
+
//# sourceMappingURL=gentask-client.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"gentask-client.js","sourceRoot":"","sources":["../src/gentask-client.ts"],"names":[],"mappings":"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"}
|
package/dist/index.d.ts
ADDED
package/dist/index.js
ADDED
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
import { run } from './cli.js';
|
|
3
|
+
/**
|
|
4
|
+
* 명령이 들어오는 자리.
|
|
5
|
+
*
|
|
6
|
+
* <p>기계가 읽는 것은 표준출력으로, 사람에게 하는 말은 표준오류로 낸다. 파이프로 이어 붙일 때
|
|
7
|
+
* 안내나 오류가 섞이면 받는 쪽이 그것까지 파싱한다.
|
|
8
|
+
*/
|
|
9
|
+
async function main() {
|
|
10
|
+
const outcome = await run(process.argv.slice(2));
|
|
11
|
+
if (outcome.out) {
|
|
12
|
+
process.stdout.write(`${outcome.out}\n`);
|
|
13
|
+
}
|
|
14
|
+
process.exitCode = outcome.code;
|
|
15
|
+
}
|
|
16
|
+
main().catch((error) => {
|
|
17
|
+
process.stderr.write(`${error instanceof Error ? error.message : String(error)}\n`);
|
|
18
|
+
process.exit(1);
|
|
19
|
+
});
|
|
20
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":";AACA,OAAO,EAAE,GAAG,EAAE,MAAM,UAAU,CAAC;AAE/B;;;;;GAKG;AACH,KAAK,UAAU,IAAI;IACjB,MAAM,OAAO,GAAG,MAAM,GAAG,CAAC,OAAO,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,CAAC;IACjD,IAAI,OAAO,CAAC,GAAG,EAAE,CAAC;QAChB,OAAO,CAAC,MAAM,CAAC,KAAK,CAAC,GAAG,OAAO,CAAC,GAAG,IAAI,CAAC,CAAC;IAC3C,CAAC;IACD,OAAO,CAAC,QAAQ,GAAG,OAAO,CAAC,IAAI,CAAC;AAClC,CAAC;AAED,IAAI,EAAE,CAAC,KAAK,CAAC,CAAC,KAAc,EAAE,EAAE;IAC9B,OAAO,CAAC,MAAM,CAAC,KAAK,CAAC,GAAG,KAAK,YAAY,KAAK,CAAC,CAAC,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC,CAAC,MAAM,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC;IACpF,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;AAClB,CAAC,CAAC,CAAC"}
|
package/package.json
ADDED
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "gentask",
|
|
3
|
+
"version": "0.1.0",
|
|
4
|
+
"type": "module",
|
|
5
|
+
"description": "gentask 의 작업을 명령줄에서 다룹니다",
|
|
6
|
+
"bin": {
|
|
7
|
+
"gentask": "./dist/index.js"
|
|
8
|
+
},
|
|
9
|
+
"scripts": {
|
|
10
|
+
"build": "tsc",
|
|
11
|
+
"test": "vitest run",
|
|
12
|
+
"check": "npm test && npm run build",
|
|
13
|
+
"prepublishOnly": "npm run check"
|
|
14
|
+
},
|
|
15
|
+
"devDependencies": {
|
|
16
|
+
"@types/node": "^22.10.2",
|
|
17
|
+
"api-types": "*",
|
|
18
|
+
"typescript": "~6.0.2",
|
|
19
|
+
"vitest": "^4.0.8"
|
|
20
|
+
},
|
|
21
|
+
"license": "MIT",
|
|
22
|
+
"keywords": [
|
|
23
|
+
"cli",
|
|
24
|
+
"todo",
|
|
25
|
+
"task",
|
|
26
|
+
"gentask"
|
|
27
|
+
],
|
|
28
|
+
"repository": {
|
|
29
|
+
"type": "git",
|
|
30
|
+
"url": "git+https://github.com/dev-goraebap/gentask.git",
|
|
31
|
+
"directory": "clients/apps/cli"
|
|
32
|
+
},
|
|
33
|
+
"homepage": "https://gentask.xyz",
|
|
34
|
+
"engines": {
|
|
35
|
+
"node": ">=20"
|
|
36
|
+
},
|
|
37
|
+
"files": [
|
|
38
|
+
"dist",
|
|
39
|
+
"README.md",
|
|
40
|
+
"LICENSE"
|
|
41
|
+
],
|
|
42
|
+
"publishConfig": {
|
|
43
|
+
"access": "public"
|
|
44
|
+
}
|
|
45
|
+
}
|