doque 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.
Files changed (41) hide show
  1. package/dist/api/auth.d.ts +18 -0
  2. package/dist/api/auth.d.ts.map +1 -0
  3. package/dist/api/auth.js +58 -0
  4. package/dist/api/auth.js.map +1 -0
  5. package/dist/api/client.d.ts +24 -0
  6. package/dist/api/client.d.ts.map +1 -0
  7. package/dist/api/client.js +133 -0
  8. package/dist/api/client.js.map +1 -0
  9. package/dist/commands/auth.d.ts +3 -0
  10. package/dist/commands/auth.d.ts.map +1 -0
  11. package/dist/commands/auth.js +188 -0
  12. package/dist/commands/auth.js.map +1 -0
  13. package/dist/commands/cargo.d.ts +3 -0
  14. package/dist/commands/cargo.d.ts.map +1 -0
  15. package/dist/commands/cargo.js +171 -0
  16. package/dist/commands/cargo.js.map +1 -0
  17. package/dist/commands/config.d.ts +3 -0
  18. package/dist/commands/config.d.ts.map +1 -0
  19. package/dist/commands/config.js +71 -0
  20. package/dist/commands/config.js.map +1 -0
  21. package/dist/commands/vessel.d.ts +3 -0
  22. package/dist/commands/vessel.d.ts.map +1 -0
  23. package/dist/commands/vessel.js +55 -0
  24. package/dist/commands/vessel.js.map +1 -0
  25. package/dist/commands/whoami.d.ts +3 -0
  26. package/dist/commands/whoami.d.ts.map +1 -0
  27. package/dist/commands/whoami.js +24 -0
  28. package/dist/commands/whoami.js.map +1 -0
  29. package/dist/config/store.d.ts +41 -0
  30. package/dist/config/store.d.ts.map +1 -0
  31. package/dist/config/store.js +78 -0
  32. package/dist/config/store.js.map +1 -0
  33. package/dist/index.d.ts +3 -0
  34. package/dist/index.d.ts.map +1 -0
  35. package/dist/index.js +23 -0
  36. package/dist/index.js.map +1 -0
  37. package/dist/utils/output.d.ts +4 -0
  38. package/dist/utils/output.d.ts.map +1 -0
  39. package/dist/utils/output.js +33 -0
  40. package/dist/utils/output.js.map +1 -0
  41. package/package.json +39 -0
@@ -0,0 +1,18 @@
1
+ export interface DeviceCodeResponse {
2
+ device_code: string;
3
+ user_code: string;
4
+ verification_uri: string;
5
+ verification_uri_complete: string;
6
+ expires_in: number;
7
+ interval: number;
8
+ }
9
+ export interface TokenResponse {
10
+ access_token: string;
11
+ refresh_token?: string;
12
+ token_type: string;
13
+ expires_in: number;
14
+ }
15
+ export declare function requestDeviceCode(authServer: string): Promise<DeviceCodeResponse>;
16
+ export declare function pollForToken(authServer: string, deviceCode: string, interval: number, expiresIn: number): Promise<TokenResponse>;
17
+ export declare function decodeJwtPayload(token: string): Record<string, unknown>;
18
+ //# sourceMappingURL=auth.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"auth.d.ts","sourceRoot":"","sources":["../../src/api/auth.ts"],"names":[],"mappings":"AAEA,MAAM,WAAW,kBAAkB;IACjC,WAAW,EAAE,MAAM,CAAC;IACpB,SAAS,EAAE,MAAM,CAAC;IAClB,gBAAgB,EAAE,MAAM,CAAC;IACzB,yBAAyB,EAAE,MAAM,CAAC;IAClC,UAAU,EAAE,MAAM,CAAC;IACnB,QAAQ,EAAE,MAAM,CAAC;CAClB;AAED,MAAM,WAAW,aAAa;IAC5B,YAAY,EAAE,MAAM,CAAC;IACrB,aAAa,CAAC,EAAE,MAAM,CAAC;IACvB,UAAU,EAAE,MAAM,CAAC;IACnB,UAAU,EAAE,MAAM,CAAC;CACpB;AAOD,wBAAsB,iBAAiB,CAAC,UAAU,EAAE,MAAM,GAAG,OAAO,CAAC,kBAAkB,CAAC,CAgBvF;AAED,wBAAsB,YAAY,CAChC,UAAU,EAAE,MAAM,EAClB,UAAU,EAAE,MAAM,EAClB,QAAQ,EAAE,MAAM,EAChB,SAAS,EAAE,MAAM,GAChB,OAAO,CAAC,aAAa,CAAC,CAmCxB;AAED,wBAAgB,gBAAgB,CAAC,KAAK,EAAE,MAAM,GAAG,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAMvE"}
@@ -0,0 +1,58 @@
1
+ // Device Authorization Grant flow (SESSION-002 검증 기반)
2
+ export async function requestDeviceCode(authServer) {
3
+ const res = await fetch(`${authServer}/oauth2/device_authorization`, {
4
+ method: 'POST',
5
+ headers: { 'Content-Type': 'application/x-www-form-urlencoded' },
6
+ body: 'client_id=doque-cli&scope=read+write',
7
+ });
8
+ if (!res.ok) {
9
+ const body = await res.json().catch(() => ({}));
10
+ const msg = body.error === 'invalid_client'
11
+ ? '인증 서버 설정 오류. 관리자에게 문의하세요.'
12
+ : (body.error ?? `HTTP ${res.status}`);
13
+ throw new Error(msg);
14
+ }
15
+ return res.json();
16
+ }
17
+ export async function pollForToken(authServer, deviceCode, interval, expiresIn) {
18
+ const deadline = Date.now() + expiresIn * 1000;
19
+ let currentInterval = interval;
20
+ while (Date.now() < deadline) {
21
+ await sleep(currentInterval * 1000);
22
+ const res = await fetch(`${authServer}/oauth2/token`, {
23
+ method: 'POST',
24
+ headers: { 'Content-Type': 'application/x-www-form-urlencoded' },
25
+ body: `grant_type=urn:ietf:params:oauth:grant-type:device_code&device_code=${encodeURIComponent(deviceCode)}&client_id=doque-cli`,
26
+ });
27
+ if (res.ok) {
28
+ return res.json();
29
+ }
30
+ const body = await res.json().catch(() => ({}));
31
+ switch (body.error) {
32
+ case 'authorization_pending':
33
+ break; // 계속 폴링
34
+ case 'slow_down':
35
+ currentInterval += 5;
36
+ break;
37
+ case 'access_denied':
38
+ throw new Error('인증이 거부되었습니다.');
39
+ case 'expired_token':
40
+ throw new Error('인증 코드가 만료되었습니다. 다시 시도하세요.');
41
+ default:
42
+ throw new Error(body.error_description ?? body.error ?? `HTTP ${res.status}`);
43
+ }
44
+ }
45
+ throw new Error('인증 시간이 초과되었습니다. 다시 시도하세요.');
46
+ }
47
+ export function decodeJwtPayload(token) {
48
+ const parts = token.split('.');
49
+ if (parts.length !== 3)
50
+ throw new Error('Invalid JWT format');
51
+ const payload = parts[1];
52
+ const decoded = Buffer.from(payload, 'base64url').toString('utf-8');
53
+ return JSON.parse(decoded);
54
+ }
55
+ function sleep(ms) {
56
+ return new Promise((resolve) => setTimeout(resolve, ms));
57
+ }
58
+ //# sourceMappingURL=auth.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"auth.js","sourceRoot":"","sources":["../../src/api/auth.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,24 @@
1
+ export interface GlobalOptions {
2
+ server?: string;
3
+ token?: string;
4
+ namespace?: string;
5
+ format?: string;
6
+ }
7
+ export interface VesselSummary {
8
+ id: number;
9
+ slug: string;
10
+ name: string;
11
+ status: string;
12
+ }
13
+ export declare function createApiClient(globalOpts: GlobalOptions): {
14
+ request: <T>(method: string, urlPath: string, body?: unknown) => Promise<T>;
15
+ get: <T>(urlPath: string) => Promise<T>;
16
+ post: <T>(urlPath: string, body: unknown) => Promise<T>;
17
+ patch: <T>(urlPath: string, body: unknown) => Promise<T>;
18
+ resolveNamespace: (nsOverride?: string) => Promise<string>;
19
+ resolveVesselId: (nsSlug: string, vesselSlug: string) => Promise<number>;
20
+ token: string;
21
+ server: string;
22
+ };
23
+ export declare function apiRequestWithToken<T>(server: string, method: string, urlPath: string, token: string, body?: unknown): Promise<T>;
24
+ //# sourceMappingURL=client.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"client.d.ts","sourceRoot":"","sources":["../../src/api/client.ts"],"names":[],"mappings":"AAEA,MAAM,WAAW,aAAa;IAC5B,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,MAAM,CAAC,EAAE,MAAM,CAAC;CACjB;AAiDD,MAAM,WAAW,aAAa;IAC5B,EAAE,EAAE,MAAM,CAAC;IACX,IAAI,EAAE,MAAM,CAAC;IACb,IAAI,EAAE,MAAM,CAAC;IACb,MAAM,EAAE,MAAM,CAAC;CAChB;AAED,wBAAgB,eAAe,CAAC,UAAU,EAAE,aAAa;cAWhC,CAAC,UAAU,MAAM,WAAW,MAAM,SAAS,OAAO,KAAG,OAAO,CAAC,CAAC,CAAC;UAiD9E,CAAC,WAAW,MAAM;WACjB,CAAC,WAAW,MAAM,QAAQ,OAAO;YAChC,CAAC,WAAW,MAAM,QAAQ,OAAO;oCA9BE,MAAM,KAAG,OAAO,CAAC,MAAM,CAAC;8BAU9B,MAAM,cAAc,MAAM,KAAG,OAAO,CAAC,MAAM,CAAC;;;EA0BpF;AAGD,wBAAsB,mBAAmB,CAAC,CAAC,EACzC,MAAM,EAAE,MAAM,EACd,MAAM,EAAE,MAAM,EACd,OAAO,EAAE,MAAM,EACf,KAAK,EAAE,MAAM,EACb,IAAI,CAAC,EAAE,OAAO,GACb,OAAO,CAAC,CAAC,CAAC,CAmBZ"}
@@ -0,0 +1,133 @@
1
+ import { configStore } from '../config/store.js';
2
+ function resolveToken(opts) {
3
+ if (process.env.DOQUE_TOKEN)
4
+ return process.env.DOQUE_TOKEN;
5
+ if (opts.token)
6
+ return opts.token;
7
+ return configStore.getToken();
8
+ }
9
+ function resolveServer(opts) {
10
+ if (opts.server)
11
+ return opts.server;
12
+ return configStore.get('server') ?? 'https://doque-api.rn00n.com';
13
+ }
14
+ async function handleErrorResponse(res) {
15
+ let detail = `HTTP ${res.status}`;
16
+ try {
17
+ const body = await res.json();
18
+ detail = body.detail ?? body.title ?? detail;
19
+ }
20
+ catch { /* ignore */ }
21
+ switch (res.status) {
22
+ case 400:
23
+ console.error(`! 잘못된 요청: ${detail}`);
24
+ break;
25
+ case 401:
26
+ console.error("! 인증이 만료되었습니다. 'doque auth login'을 실행하세요.");
27
+ break;
28
+ case 403:
29
+ console.error('! 권한이 없습니다.');
30
+ break;
31
+ case 404:
32
+ console.error(`! 찾을 수 없습니다: ${detail}`);
33
+ break;
34
+ case 409:
35
+ console.error(`! 이미 존재합니다: ${detail}`);
36
+ break;
37
+ default:
38
+ console.error(`! 서버 오류가 발생했습니다. 잠시 후 다시 시도하세요. (${res.status})`);
39
+ }
40
+ process.exit(1);
41
+ }
42
+ export function createApiClient(globalOpts) {
43
+ const server = resolveServer(globalOpts);
44
+ const token = resolveToken(globalOpts);
45
+ if (!token) {
46
+ console.error("! 인증이 필요합니다. 'doque auth login'을 실행하세요.");
47
+ process.exit(1);
48
+ }
49
+ const resolvedToken = token;
50
+ async function request(method, urlPath, body) {
51
+ let res;
52
+ try {
53
+ res = await fetch(`${server}${urlPath}`, {
54
+ method,
55
+ headers: {
56
+ Authorization: `Bearer ${resolvedToken}`,
57
+ 'Content-Type': 'application/json',
58
+ },
59
+ body: body !== undefined ? JSON.stringify(body) : undefined,
60
+ });
61
+ }
62
+ catch {
63
+ console.error(`! 서버에 연결할 수 없습니다: ${server}`);
64
+ process.exit(1);
65
+ }
66
+ if (!res.ok)
67
+ return handleErrorResponse(res);
68
+ if (res.status === 204)
69
+ return undefined;
70
+ return res.json();
71
+ }
72
+ async function resolveNamespace(nsOverride) {
73
+ if (nsOverride)
74
+ return nsOverride;
75
+ const globalNs = globalOpts.namespace;
76
+ if (globalNs)
77
+ return globalNs;
78
+ const ns = configStore.load().user?.namespace;
79
+ if (ns)
80
+ return ns;
81
+ console.error("! namespace가 설정되지 않았습니다. 'doque auth login'으로 로그인하세요.");
82
+ process.exit(1);
83
+ }
84
+ async function resolveVesselId(nsSlug, vesselSlug) {
85
+ const cached = configStore.getCachedVesselId(vesselSlug);
86
+ if (cached !== null)
87
+ return cached;
88
+ const vessels = await request('GET', `/api/namespaces/${nsSlug}/vessels`);
89
+ for (const v of vessels) {
90
+ configStore.cacheVessel(v.slug, v.id);
91
+ }
92
+ const found = vessels.find((v) => v.slug === vesselSlug);
93
+ if (!found) {
94
+ console.error(`! Vessel을 찾을 수 없습니다: ${vesselSlug}`);
95
+ process.exit(1);
96
+ }
97
+ return found.id;
98
+ }
99
+ return {
100
+ request,
101
+ get: (urlPath) => request('GET', urlPath),
102
+ post: (urlPath, body) => request('POST', urlPath, body),
103
+ patch: (urlPath, body) => request('PATCH', urlPath, body),
104
+ resolveNamespace,
105
+ resolveVesselId,
106
+ token: resolvedToken,
107
+ server,
108
+ };
109
+ }
110
+ // auth 전용 API 요청 (토큰 없이, 또는 커스텀 토큰으로)
111
+ export async function apiRequestWithToken(server, method, urlPath, token, body) {
112
+ let res;
113
+ try {
114
+ res = await fetch(`${server}${urlPath}`, {
115
+ method,
116
+ headers: {
117
+ Authorization: `Bearer ${token}`,
118
+ 'Content-Type': 'application/json',
119
+ },
120
+ body: body !== undefined ? JSON.stringify(body) : undefined,
121
+ });
122
+ }
123
+ catch {
124
+ console.error(`! 서버에 연결할 수 없습니다: ${server}`);
125
+ process.exit(1);
126
+ }
127
+ if (!res.ok)
128
+ return handleErrorResponse(res);
129
+ if (res.status === 204)
130
+ return undefined;
131
+ return res.json();
132
+ }
133
+ //# sourceMappingURL=client.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"client.js","sourceRoot":"","sources":["../../src/api/client.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,3 @@
1
+ import { Command } from 'commander';
2
+ export declare function registerAuthCommands(program: Command): void;
3
+ //# sourceMappingURL=auth.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"auth.d.ts","sourceRoot":"","sources":["../../src/commands/auth.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AA4CpC,wBAAgB,oBAAoB,CAAC,OAAO,EAAE,OAAO,GAAG,IAAI,CA4K3D"}
@@ -0,0 +1,188 @@
1
+ import chalk from 'chalk';
2
+ import ora from 'ora';
3
+ import * as os from 'os';
4
+ import * as crypto from 'crypto';
5
+ import { configStore } from '../config/store.js';
6
+ import { requestDeviceCode, pollForToken, decodeJwtPayload } from '../api/auth.js';
7
+ import { apiRequestWithToken } from '../api/client.js';
8
+ async function registerBeacon(server, token) {
9
+ try {
10
+ const config = configStore.load();
11
+ let deviceId = config.device?.id;
12
+ if (!deviceId) {
13
+ deviceId = crypto.randomUUID();
14
+ configStore.set('device', { id: deviceId });
15
+ }
16
+ await fetch(`${server}/api/me/beacons/device`, {
17
+ method: 'PUT',
18
+ headers: {
19
+ Authorization: `Bearer ${token}`,
20
+ 'Content-Type': 'application/json',
21
+ },
22
+ body: JSON.stringify({
23
+ deviceId,
24
+ name: os.hostname(),
25
+ type: 'cli',
26
+ }),
27
+ });
28
+ }
29
+ catch {
30
+ // non-blocking: 실패해도 인증 플로우 중단하지 않음
31
+ }
32
+ }
33
+ export function registerAuthCommands(program) {
34
+ const auth = program.command('auth').description('인증 관리');
35
+ // doque auth login [--token <pat>]
36
+ auth
37
+ .command('login')
38
+ .description('로그인 (Device Flow 기본, --token으로 PAT 지정)')
39
+ .option('-t, --token <token>', 'Personal Access Token 직접 입력')
40
+ .action(async (opts, cmd) => {
41
+ const globalOpts = cmd.optsWithGlobals();
42
+ const patToken = opts.token ?? globalOpts.token;
43
+ const server = globalOpts.server ?? configStore.get('server') ?? 'https://doque-api.rn00n.com';
44
+ if (patToken) {
45
+ // PAT 로그인
46
+ const spinner = ora('토큰 검증 중...').start();
47
+ try {
48
+ const me = await apiRequestWithToken(server, 'GET', '/api/me', patToken);
49
+ const username = me.displayName;
50
+ const config = configStore.load();
51
+ config.auth = { type: 'pat', access_token: patToken };
52
+ config.user = { id: me.id, username, namespace: username };
53
+ configStore.save(config);
54
+ spinner.succeed(chalk.green(`✓ 인증 완료! @${username} 으로 로그인됨`));
55
+ console.log(` 토큰 저장: ~/.doque/config.json`);
56
+ registerBeacon(server, patToken).catch(() => { });
57
+ }
58
+ catch {
59
+ spinner.fail('토큰 검증 실패');
60
+ process.exit(1);
61
+ }
62
+ return;
63
+ }
64
+ // Device Flow 로그인
65
+ const authServer = configStore.get('authServer') ?? 'https://auth.rn00n.com';
66
+ let deviceCodeResp;
67
+ try {
68
+ deviceCodeResp = await requestDeviceCode(authServer);
69
+ }
70
+ catch (err) {
71
+ console.error(`! 인증 서버에 연결할 수 없습니다: ${authServer}`);
72
+ if (err instanceof Error)
73
+ console.error(` ${err.message}`);
74
+ process.exit(1);
75
+ }
76
+ const { device_code, user_code, verification_uri, verification_uri_complete, expires_in, interval } = deviceCodeResp;
77
+ console.log();
78
+ console.log(chalk.yellow('! 브라우저에서 아래 URL을 열고 코드를 입력하세요:'));
79
+ console.log(` ${chalk.cyan(verification_uri)}`);
80
+ console.log();
81
+ console.log(` 코드: ${chalk.bold.green(user_code)}`);
82
+ console.log();
83
+ // 브라우저 열기 시도 (실패해도 무시)
84
+ try {
85
+ const { default: open } = await import('open');
86
+ await open(verification_uri_complete);
87
+ }
88
+ catch {
89
+ // 브라우저가 없는 환경
90
+ }
91
+ const spinner = ora('브라우저에서 승인을 기다리는 중...').start();
92
+ let tokenResp;
93
+ try {
94
+ tokenResp = await pollForToken(authServer, device_code, interval ?? 5, expires_in);
95
+ }
96
+ catch (err) {
97
+ spinner.fail('인증 실패');
98
+ if (err instanceof Error)
99
+ console.error(`! ${err.message}`);
100
+ process.exit(1);
101
+ }
102
+ spinner.succeed('인증 성공!');
103
+ // JWT decode → username, expires_at 추출
104
+ let username = '';
105
+ let expiresAt;
106
+ try {
107
+ const payload = decodeJwtPayload(tokenResp.access_token);
108
+ username = payload['sub'] ?? '';
109
+ const exp = payload['exp'];
110
+ if (exp)
111
+ expiresAt = new Date(exp * 1000).toISOString();
112
+ }
113
+ catch {
114
+ // decode 실패 시 계속 진행
115
+ }
116
+ // GET /api/me → member 정보 조회
117
+ let memberId = 0;
118
+ try {
119
+ const me = await apiRequestWithToken(server, 'GET', '/api/me', tokenResp.access_token);
120
+ memberId = me.id;
121
+ if (!username)
122
+ username = me.displayName;
123
+ }
124
+ catch {
125
+ // API 호출 실패해도 계속
126
+ }
127
+ // config.json 저장
128
+ const config = configStore.load();
129
+ config.auth = {
130
+ type: 'device',
131
+ access_token: tokenResp.access_token,
132
+ refresh_token: tokenResp.refresh_token,
133
+ expires_at: expiresAt,
134
+ };
135
+ config.user = {
136
+ id: memberId,
137
+ username,
138
+ namespace: username,
139
+ };
140
+ configStore.save(config);
141
+ console.log(chalk.green(`✓ 인증 완료! @${username} 으로 로그인됨`));
142
+ console.log(` 토큰 저장: ~/.doque/config.json`);
143
+ // Beacon 자동 등록 (non-blocking)
144
+ registerBeacon(server, tokenResp.access_token).catch(() => { });
145
+ });
146
+ // doque auth status
147
+ auth
148
+ .command('status')
149
+ .description('인증 상태 확인')
150
+ .action(async (_opts, cmd) => {
151
+ const globalOpts = cmd.optsWithGlobals();
152
+ const server = globalOpts.server ?? configStore.get('server') ?? 'https://doque-api.rn00n.com';
153
+ const token = process.env.DOQUE_TOKEN ?? globalOpts.token ?? configStore.getToken();
154
+ if (!token) {
155
+ console.log(chalk.red('✗ 로그인되지 않음'));
156
+ console.log(" 'doque auth login'을 실행하세요.");
157
+ return;
158
+ }
159
+ try {
160
+ const me = await apiRequestWithToken(server, 'GET', '/api/me', token);
161
+ const config = configStore.load();
162
+ const authType = config.auth?.type ?? 'unknown';
163
+ const expiresAt = config.auth?.expires_at;
164
+ console.log(chalk.green('✓ 로그인됨'));
165
+ console.log(` 사용자: ${me.displayName}`);
166
+ console.log(` 인증 방식: ${authType}`);
167
+ if (expiresAt) {
168
+ console.log(` 만료: ${new Date(expiresAt).toLocaleString()}`);
169
+ }
170
+ }
171
+ catch {
172
+ console.log(chalk.red('✗ 토큰이 만료되었거나 유효하지 않습니다'));
173
+ console.log(" 'doque auth login'을 실행하세요.");
174
+ }
175
+ });
176
+ // doque auth logout
177
+ auth
178
+ .command('logout')
179
+ .description('로그아웃')
180
+ .action(() => {
181
+ const config = configStore.load();
182
+ delete config.auth;
183
+ delete config.user;
184
+ configStore.save(config);
185
+ console.log(chalk.green('✓ 로그아웃됨'));
186
+ });
187
+ }
188
+ //# sourceMappingURL=auth.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"auth.js","sourceRoot":"","sources":["../../src/commands/auth.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,3 @@
1
+ import { Command } from 'commander';
2
+ export declare function registerCargoCommands(program: Command): void;
3
+ //# sourceMappingURL=cargo.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"cargo.d.ts","sourceRoot":"","sources":["../../src/commands/cargo.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AAyDpC,wBAAgB,qBAAqB,CAAC,OAAO,EAAE,OAAO,GAAG,IAAI,CAgK5D"}
@@ -0,0 +1,171 @@
1
+ import chalk from 'chalk';
2
+ import { configStore } from '../config/store.js';
3
+ import { createApiClient } from '../api/client.js';
4
+ import { formatTable, formatJson, formatDetail } from '../utils/output.js';
5
+ function flattenCargos(cargos) {
6
+ const result = [];
7
+ for (const c of cargos) {
8
+ result.push(c);
9
+ if (c.children.length > 0) {
10
+ result.push(...flattenCargos(c.children));
11
+ }
12
+ }
13
+ return result;
14
+ }
15
+ async function getVesselId(client, vesselSlug, namespace) {
16
+ if (!vesselSlug) {
17
+ // 캐시에서 첫 번째 vessel 사용
18
+ const cached = configStore.load().cache?.vessels;
19
+ if (cached) {
20
+ const entries = Object.entries(cached);
21
+ if (entries.length === 1)
22
+ return entries[0][1].id;
23
+ if (entries.length > 1) {
24
+ console.error('! --vessel 옵션을 지정하세요. 예: doque cargo list --vessel my-vessel');
25
+ process.exit(1);
26
+ }
27
+ }
28
+ console.error('! --vessel 옵션을 지정하세요. 예: doque cargo list --vessel my-vessel');
29
+ process.exit(1);
30
+ }
31
+ return client.resolveVesselId(namespace, vesselSlug);
32
+ }
33
+ export function registerCargoCommands(program) {
34
+ const cargo = program.command('cargo').description('Cargo(작업) 관리');
35
+ // doque cargo list [-v vessel] [-s status] [--type type] [-f format]
36
+ cargo
37
+ .command('list')
38
+ .description('Cargo 목록 조회')
39
+ .option('-v, --vessel <slug>', 'Vessel slug 지정')
40
+ .option('-s, --status <status>', '상태 필터 (backlog|todo|in_progress|review|done)')
41
+ .option('--type <type>', '타입 필터 (epic|story|task)')
42
+ .option('-f, --format <format>', '출력 형식 (table|json)', 'table')
43
+ .action(async (opts, cmd) => {
44
+ const globalOpts = cmd.optsWithGlobals();
45
+ const client = createApiClient(globalOpts);
46
+ const ns = await client.resolveNamespace(globalOpts.namespace);
47
+ const vesselId = await getVesselId(client, opts.vessel, ns);
48
+ const params = new URLSearchParams();
49
+ if (opts.status)
50
+ params.set('status', opts.status);
51
+ if (opts.type)
52
+ params.set('type', opts.type);
53
+ const query = params.size > 0 ? `?${params.toString()}` : '';
54
+ const cargos = await client.get(`/api/vessels/${vesselId}/cargos${query}`);
55
+ const flat = flattenCargos(cargos);
56
+ const format = opts.format ?? globalOpts.format ?? 'table';
57
+ if (format === 'json') {
58
+ formatJson(flat);
59
+ return;
60
+ }
61
+ formatTable(flat.map((c) => ({
62
+ id: c.id,
63
+ type: c.type,
64
+ status: c.status,
65
+ title: c.title,
66
+ assignee: c.assigneeId ?? '-',
67
+ })), ['id', 'type', 'status', 'title', 'assignee']);
68
+ });
69
+ // doque cargo info <id> [-v vessel] [-f format]
70
+ cargo
71
+ .command('info <id>')
72
+ .description('Cargo 상세 조회')
73
+ .option('-v, --vessel <slug>', 'Vessel slug 지정')
74
+ .option('-f, --format <format>', '출력 형식 (table|json)', 'table')
75
+ .action(async (id, opts, cmd) => {
76
+ const globalOpts = cmd.optsWithGlobals();
77
+ const client = createApiClient(globalOpts);
78
+ const ns = await client.resolveNamespace(globalOpts.namespace);
79
+ const vesselId = await getVesselId(client, opts.vessel, ns);
80
+ const c = await client.get(`/api/vessels/${vesselId}/cargos/${id}`);
81
+ const format = opts.format ?? globalOpts.format ?? 'table';
82
+ if (format === 'json') {
83
+ formatJson(c);
84
+ return;
85
+ }
86
+ formatDetail(chalk.bold(`#${c.id} ${c.title}`), [
87
+ ['Type', c.type],
88
+ ['Status', c.status],
89
+ ['Description', c.description],
90
+ ['Assignee ID', c.assigneeId],
91
+ ['Priority', c.priority],
92
+ ['Due', c.dueAt ? new Date(c.dueAt).toLocaleString() : null],
93
+ ['Started', c.startedAt ? new Date(c.startedAt).toLocaleString() : null],
94
+ ]);
95
+ });
96
+ // doque cargo create --title "제목" [--type task] [-v vessel] [-d desc]
97
+ cargo
98
+ .command('create')
99
+ .description('Cargo 생성')
100
+ .requiredOption('-t, --title <title>', '제목')
101
+ .option('--type <type>', '타입 (epic|story|task)', 'task')
102
+ .option('-v, --vessel <slug>', 'Vessel slug 지정')
103
+ .option('-d, --description <desc>', '설명')
104
+ .option('--parent <id>', '부모 Cargo ID')
105
+ .option('-f, --format <format>', '출력 형식 (table|json)', 'table')
106
+ .action(async (opts, cmd) => {
107
+ const globalOpts = cmd.optsWithGlobals();
108
+ const client = createApiClient(globalOpts);
109
+ const ns = await client.resolveNamespace(globalOpts.namespace);
110
+ const vesselId = await getVesselId(client, opts.vessel, ns);
111
+ const c = await client.post(`/api/vessels/${vesselId}/cargos`, {
112
+ type: opts.type,
113
+ title: opts.title,
114
+ description: opts.description ?? null,
115
+ parentId: opts.parent ? Number(opts.parent) : null,
116
+ sortOrder: 0,
117
+ });
118
+ const format = opts.format ?? globalOpts.format ?? 'table';
119
+ if (format === 'json') {
120
+ formatJson(c);
121
+ return;
122
+ }
123
+ console.log(chalk.green(`✓ Cargo 생성됨: #${c.id} "${c.title}"`));
124
+ });
125
+ // doque cargo start <id> [-v vessel]
126
+ cargo
127
+ .command('start <id>')
128
+ .description('Cargo 시작 (→ in_progress)')
129
+ .option('-v, --vessel <slug>', 'Vessel slug 지정')
130
+ .action(async (id, opts, cmd) => {
131
+ const globalOpts = cmd.optsWithGlobals();
132
+ const client = createApiClient(globalOpts);
133
+ const ns = await client.resolveNamespace(globalOpts.namespace);
134
+ const vesselId = await getVesselId(client, opts.vessel, ns);
135
+ const c = await client.patch(`/api/vessels/${vesselId}/cargos/${id}/status`, {
136
+ status: 'in_progress',
137
+ });
138
+ console.log(chalk.green(`✓ #${c.id} "${c.title}" → ${c.status}`));
139
+ });
140
+ // doque cargo done <id> [-v vessel]
141
+ cargo
142
+ .command('done <id>')
143
+ .description('Cargo 완료 (→ done)')
144
+ .option('-v, --vessel <slug>', 'Vessel slug 지정')
145
+ .action(async (id, opts, cmd) => {
146
+ const globalOpts = cmd.optsWithGlobals();
147
+ const client = createApiClient(globalOpts);
148
+ const ns = await client.resolveNamespace(globalOpts.namespace);
149
+ const vesselId = await getVesselId(client, opts.vessel, ns);
150
+ const c = await client.patch(`/api/vessels/${vesselId}/cargos/${id}/status`, {
151
+ status: 'done',
152
+ });
153
+ console.log(chalk.green(`✓ #${c.id} "${c.title}" → ${c.status}`));
154
+ });
155
+ // doque cargo status <id> <status> [-v vessel]
156
+ cargo
157
+ .command('status <id> <status>')
158
+ .description('Cargo 상태 변경 (backlog|todo|in_progress|review|done)')
159
+ .option('-v, --vessel <slug>', 'Vessel slug 지정')
160
+ .action(async (id, status, opts, cmd) => {
161
+ const globalOpts = cmd.optsWithGlobals();
162
+ const client = createApiClient(globalOpts);
163
+ const ns = await client.resolveNamespace(globalOpts.namespace);
164
+ const vesselId = await getVesselId(client, opts.vessel, ns);
165
+ const c = await client.patch(`/api/vessels/${vesselId}/cargos/${id}/status`, {
166
+ status,
167
+ });
168
+ console.log(chalk.green(`✓ #${c.id} "${c.title}" → ${c.status}`));
169
+ });
170
+ }
171
+ //# sourceMappingURL=cargo.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"cargo.js","sourceRoot":"","sources":["../../src/commands/cargo.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,3 @@
1
+ import { Command } from 'commander';
2
+ export declare function registerConfigCommands(program: Command): void;
3
+ //# sourceMappingURL=config.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"config.d.ts","sourceRoot":"","sources":["../../src/commands/config.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AAyBpC,wBAAgB,sBAAsB,CAAC,OAAO,EAAE,OAAO,GAAG,IAAI,CAoD7D"}
@@ -0,0 +1,71 @@
1
+ import chalk from 'chalk';
2
+ import { configStore } from '../config/store.js';
3
+ function getNestedValue(obj, keys) {
4
+ let current = obj;
5
+ for (const key of keys) {
6
+ if (current == null || typeof current !== 'object')
7
+ return undefined;
8
+ current = current[key];
9
+ }
10
+ return current;
11
+ }
12
+ function setNestedValue(obj, keys, value) {
13
+ let current = obj;
14
+ for (let i = 0; i < keys.length - 1; i++) {
15
+ const key = keys[i];
16
+ if (!current[key] || typeof current[key] !== 'object') {
17
+ current[key] = {};
18
+ }
19
+ current = current[key];
20
+ }
21
+ current[keys[keys.length - 1]] = value;
22
+ }
23
+ export function registerConfigCommands(program) {
24
+ const config = program.command('config').description('설정 관리');
25
+ config
26
+ .command('get <key>')
27
+ .description('설정값 조회')
28
+ .action((key) => {
29
+ const all = configStore.getAll();
30
+ const keys = key.split('.');
31
+ const value = getNestedValue(all, keys);
32
+ if (value === undefined) {
33
+ console.error(`! 키를 찾을 수 없습니다: ${key}`);
34
+ process.exit(1);
35
+ }
36
+ console.log(typeof value === 'object' ? JSON.stringify(value, null, 2) : String(value));
37
+ });
38
+ config
39
+ .command('set <key> <value>')
40
+ .description('설정값 변경')
41
+ .action((key, value) => {
42
+ const all = configStore.getAll();
43
+ const keys = key.split('.');
44
+ setNestedValue(all, keys, value);
45
+ configStore.save(all);
46
+ console.log(chalk.green(`✓ ${key} = ${value}`));
47
+ });
48
+ config
49
+ .command('list')
50
+ .description('전체 설정 조회')
51
+ .action(() => {
52
+ const all = configStore.getAll();
53
+ // 토큰 마스킹
54
+ const display = JSON.parse(JSON.stringify(all));
55
+ if (display.auth?.access_token) {
56
+ display.auth.access_token = `${display.auth.access_token.slice(0, 20)}...`;
57
+ }
58
+ if (display.auth?.refresh_token) {
59
+ display.auth.refresh_token = `${display.auth.refresh_token.slice(0, 20)}...`;
60
+ }
61
+ console.log(JSON.stringify(display, null, 2));
62
+ });
63
+ config
64
+ .command('reset')
65
+ .description('설정 초기화 (인증 정보는 logout으로 별도 삭제)')
66
+ .action(() => {
67
+ configStore.reset();
68
+ console.log(chalk.green('✓ 설정이 기본값으로 초기화되었습니다'));
69
+ });
70
+ }
71
+ //# sourceMappingURL=config.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"config.js","sourceRoot":"","sources":["../../src/commands/config.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,3 @@
1
+ import { Command } from 'commander';
2
+ export declare function registerVesselCommands(program: Command): void;
3
+ //# sourceMappingURL=vessel.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"vessel.d.ts","sourceRoot":"","sources":["../../src/commands/vessel.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AAiBpC,wBAAgB,sBAAsB,CAAC,OAAO,EAAE,OAAO,GAAG,IAAI,CA6D7D"}
@@ -0,0 +1,55 @@
1
+ import chalk from 'chalk';
2
+ import { configStore } from '../config/store.js';
3
+ import { createApiClient } from '../api/client.js';
4
+ import { formatTable, formatJson, formatDetail } from '../utils/output.js';
5
+ export function registerVesselCommands(program) {
6
+ const vessel = program.command('vessel').description('Vessel(프로젝트) 관리');
7
+ // doque vessel list [-n namespace] [-f format]
8
+ vessel
9
+ .command('list')
10
+ .description('Vessel 목록 조회')
11
+ .option('-n, --namespace <slug>', 'Namespace 지정 (기본: 로그인 사용자)')
12
+ .option('-f, --format <format>', '출력 형식 (table|json)', 'table')
13
+ .action(async (opts, cmd) => {
14
+ const globalOpts = cmd.optsWithGlobals();
15
+ const client = createApiClient(globalOpts);
16
+ const ns = await client.resolveNamespace(opts.namespace ?? globalOpts.namespace);
17
+ const vessels = await client.get(`/api/namespaces/${ns}/vessels`);
18
+ // 캐시 업데이트
19
+ for (const v of vessels) {
20
+ configStore.cacheVessel(v.slug, v.id);
21
+ }
22
+ const format = opts.format ?? globalOpts.format ?? 'table';
23
+ if (format === 'json') {
24
+ formatJson(vessels);
25
+ return;
26
+ }
27
+ formatTable(vessels.map((v) => ({ id: v.id, slug: v.slug, name: v.name, status: v.status })), ['id', 'slug', 'name', 'status']);
28
+ });
29
+ // doque vessel info <slug> [-n namespace] [-f format]
30
+ vessel
31
+ .command('info <slug>')
32
+ .description('Vessel 상세 조회')
33
+ .option('-n, --namespace <slug>', 'Namespace 지정')
34
+ .option('-f, --format <format>', '출력 형식 (table|json)', 'table')
35
+ .action(async (slug, opts, cmd) => {
36
+ const globalOpts = cmd.optsWithGlobals();
37
+ const client = createApiClient(globalOpts);
38
+ const ns = await client.resolveNamespace(opts.namespace ?? globalOpts.namespace);
39
+ const v = await client.get(`/api/namespaces/${ns}/vessels/${slug}`);
40
+ configStore.cacheVessel(v.slug, v.id);
41
+ const format = opts.format ?? globalOpts.format ?? 'table';
42
+ if (format === 'json') {
43
+ formatJson(v);
44
+ return;
45
+ }
46
+ formatDetail(chalk.bold(`Vessel: ${v.name}`), [
47
+ ['ID', v.id],
48
+ ['Slug', v.slug],
49
+ ['Status', v.status],
50
+ ['Description', v.description],
51
+ ['Created', new Date(v.createdAt).toLocaleString()],
52
+ ]);
53
+ });
54
+ }
55
+ //# sourceMappingURL=vessel.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"vessel.js","sourceRoot":"","sources":["../../src/commands/vessel.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,3 @@
1
+ import { Command } from 'commander';
2
+ export declare function registerWhoamiCommand(program: Command): void;
3
+ //# sourceMappingURL=whoami.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"whoami.d.ts","sourceRoot":"","sources":["../../src/commands/whoami.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AAcpC,wBAAgB,qBAAqB,CAAC,OAAO,EAAE,OAAO,GAAG,IAAI,CAsB5D"}
@@ -0,0 +1,24 @@
1
+ import chalk from 'chalk';
2
+ import { configStore } from '../config/store.js';
3
+ import { createApiClient } from '../api/client.js';
4
+ export function registerWhoamiCommand(program) {
5
+ program
6
+ .command('whoami')
7
+ .description('현재 로그인된 사용자 정보')
8
+ .action(async (_opts, cmd) => {
9
+ const globalOpts = cmd.optsWithGlobals();
10
+ const client = createApiClient(globalOpts);
11
+ const me = await client.get('/api/me');
12
+ const config = configStore.load();
13
+ const namespace = config.user?.namespace ?? me.displayName;
14
+ if (globalOpts.format === 'json') {
15
+ console.log(JSON.stringify({ username: namespace, ...me }, null, 2));
16
+ return;
17
+ }
18
+ console.log(chalk.bold.green(`@${namespace}`));
19
+ console.log(` ID: ${me.id}`);
20
+ console.log(` 이름: ${me.displayName}`);
21
+ console.log(` Namespace: ${namespace}`);
22
+ });
23
+ }
24
+ //# sourceMappingURL=whoami.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"whoami.js","sourceRoot":"","sources":["../../src/commands/whoami.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,MAAM,OAAO,CAAC;AAC1B,OAAO,EAAE,WAAW,EAAE,MAAM,oBAAoB,CAAC;AACjD,OAAO,EAAE,eAAe,EAAE,MAAM,kBAAkB,CAAC;AAWnD,MAAM,UAAU,qBAAqB,CAAC,OAAgB;IACpD,OAAO;SACJ,OAAO,CAAC,QAAQ,CAAC;SACjB,WAAW,CAAC,gBAAgB,CAAC;SAC7B,MAAM,CAAC,KAAK,EAAE,KAAK,EAAE,GAAY,EAAE,EAAE;QACpC,MAAM,UAAU,GAAG,GAAG,CAAC,eAAe,EAAwD,CAAC;QAC/F,MAAM,MAAM,GAAG,eAAe,CAAC,UAAU,CAAC,CAAC;QAE3C,MAAM,EAAE,GAAG,MAAM,MAAM,CAAC,GAAG,CAAiB,SAAS,CAAC,CAAC;QACvD,MAAM,MAAM,GAAG,WAAW,CAAC,IAAI,EAAE,CAAC;QAClC,MAAM,SAAS,GAAG,MAAM,CAAC,IAAI,EAAE,SAAS,IAAI,EAAE,CAAC,WAAW,CAAC;QAE3D,IAAI,UAAU,CAAC,MAAM,KAAK,MAAM,EAAE,CAAC;YACjC,OAAO,CAAC,GAAG,CAAC,IAAI,CAAC,SAAS,CAAC,EAAE,QAAQ,EAAE,SAAS,EAAE,GAAG,EAAE,EAAE,EAAE,IAAI,EAAE,CAAC,CAAC,CAAC,CAAC;YACrE,OAAO;QACT,CAAC;QAED,OAAO,CAAC,GAAG,CAAC,KAAK,CAAC,IAAI,CAAC,KAAK,CAAC,IAAI,SAAS,EAAE,CAAC,CAAC,CAAC;QAC/C,OAAO,CAAC,GAAG,CAAC,SAAS,EAAE,CAAC,EAAE,EAAE,CAAC,CAAC;QAC9B,OAAO,CAAC,GAAG,CAAC,SAAS,EAAE,CAAC,WAAW,EAAE,CAAC,CAAC;QACvC,OAAO,CAAC,GAAG,CAAC,gBAAgB,SAAS,EAAE,CAAC,CAAC;IAC3C,CAAC,CAAC,CAAC;AACP,CAAC"}
@@ -0,0 +1,41 @@
1
+ export interface AuthConfig {
2
+ type: 'device' | 'pat';
3
+ access_token: string;
4
+ refresh_token?: string;
5
+ expires_at?: string;
6
+ }
7
+ export interface UserConfig {
8
+ id: number;
9
+ username: string;
10
+ namespace: string;
11
+ }
12
+ export interface DoqueConfig {
13
+ server: string;
14
+ authServer: string;
15
+ auth?: AuthConfig;
16
+ user?: UserConfig;
17
+ device?: {
18
+ id: string;
19
+ };
20
+ cache?: {
21
+ vessels?: Record<string, {
22
+ id: number;
23
+ cached_at: string;
24
+ }>;
25
+ };
26
+ }
27
+ declare function load(): DoqueConfig;
28
+ declare function save(config: DoqueConfig): void;
29
+ export declare const configStore: {
30
+ load: typeof load;
31
+ save: typeof save;
32
+ getAll(): DoqueConfig;
33
+ get<K extends keyof DoqueConfig>(key: K): DoqueConfig[K];
34
+ set<K extends keyof DoqueConfig>(key: K, value: DoqueConfig[K]): void;
35
+ reset(): void;
36
+ getToken(): string | null;
37
+ cacheVessel(slug: string, id: number): void;
38
+ getCachedVesselId(slug: string): number | null;
39
+ };
40
+ export {};
41
+ //# sourceMappingURL=store.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"store.d.ts","sourceRoot":"","sources":["../../src/config/store.ts"],"names":[],"mappings":"AAOA,MAAM,WAAW,UAAU;IACzB,IAAI,EAAE,QAAQ,GAAG,KAAK,CAAC;IACvB,YAAY,EAAE,MAAM,CAAC;IACrB,aAAa,CAAC,EAAE,MAAM,CAAC;IACvB,UAAU,CAAC,EAAE,MAAM,CAAC;CACrB;AAED,MAAM,WAAW,UAAU;IACzB,EAAE,EAAE,MAAM,CAAC;IACX,QAAQ,EAAE,MAAM,CAAC;IACjB,SAAS,EAAE,MAAM,CAAC;CACnB;AAED,MAAM,WAAW,WAAW;IAC1B,MAAM,EAAE,MAAM,CAAC;IACf,UAAU,EAAE,MAAM,CAAC;IACnB,IAAI,CAAC,EAAE,UAAU,CAAC;IAClB,IAAI,CAAC,EAAE,UAAU,CAAC;IAClB,MAAM,CAAC,EAAE;QAAE,EAAE,EAAE,MAAM,CAAA;KAAE,CAAC;IACxB,KAAK,CAAC,EAAE;QACN,OAAO,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE;YAAE,EAAE,EAAE,MAAM,CAAC;YAAC,SAAS,EAAE,MAAM,CAAA;SAAE,CAAC,CAAC;KAC7D,CAAC;CACH;AAaD,iBAAS,IAAI,IAAI,WAAW,CAW3B;AAED,iBAAS,IAAI,CAAC,MAAM,EAAE,WAAW,GAAG,IAAI,CAMvC;AAED,eAAO,MAAM,WAAW;;;cAGZ,WAAW;QAGjB,CAAC,SAAS,MAAM,WAAW,OAAO,CAAC,GAAG,WAAW,CAAC,CAAC,CAAC;QAGpD,CAAC,SAAS,MAAM,WAAW,OAAO,CAAC,SAAS,WAAW,CAAC,CAAC,CAAC,GAAG,IAAI;aAK5D,IAAI;gBAUD,MAAM,GAAG,IAAI;sBAIP,MAAM,MAAM,MAAM,GAAG,IAAI;4BAOnB,MAAM,GAAG,MAAM,GAAG,IAAI;CAI/C,CAAC"}
@@ -0,0 +1,78 @@
1
+ import * as fs from 'fs';
2
+ import * as os from 'os';
3
+ import * as path from 'path';
4
+ const CONFIG_DIR = path.join(os.homedir(), '.doque');
5
+ const CONFIG_PATH = path.join(CONFIG_DIR, 'config.json');
6
+ const DEFAULTS = {
7
+ server: 'https://doque-api.rn00n.com',
8
+ authServer: 'https://auth.rn00n.com',
9
+ };
10
+ function ensureDir() {
11
+ if (!fs.existsSync(CONFIG_DIR)) {
12
+ fs.mkdirSync(CONFIG_DIR, { recursive: true, mode: 0o700 });
13
+ }
14
+ }
15
+ function load() {
16
+ if (!fs.existsSync(CONFIG_PATH)) {
17
+ return { ...DEFAULTS };
18
+ }
19
+ try {
20
+ const raw = fs.readFileSync(CONFIG_PATH, 'utf-8');
21
+ const parsed = JSON.parse(raw);
22
+ return { ...DEFAULTS, ...parsed };
23
+ }
24
+ catch {
25
+ return { ...DEFAULTS };
26
+ }
27
+ }
28
+ function save(config) {
29
+ ensureDir();
30
+ fs.writeFileSync(CONFIG_PATH, JSON.stringify(config, null, 2), {
31
+ mode: 0o600,
32
+ encoding: 'utf-8',
33
+ });
34
+ }
35
+ export const configStore = {
36
+ load,
37
+ save,
38
+ getAll() {
39
+ return load();
40
+ },
41
+ get(key) {
42
+ return load()[key];
43
+ },
44
+ set(key, value) {
45
+ const config = load();
46
+ config[key] = value;
47
+ save(config);
48
+ },
49
+ reset() {
50
+ const current = load();
51
+ const preserved = {
52
+ ...DEFAULTS,
53
+ auth: current.auth,
54
+ user: current.user,
55
+ device: current.device,
56
+ };
57
+ save(preserved);
58
+ },
59
+ getToken() {
60
+ if (process.env.DOQUE_TOKEN)
61
+ return process.env.DOQUE_TOKEN;
62
+ return load().auth?.access_token ?? null;
63
+ },
64
+ cacheVessel(slug, id) {
65
+ const config = load();
66
+ if (!config.cache)
67
+ config.cache = {};
68
+ if (!config.cache.vessels)
69
+ config.cache.vessels = {};
70
+ config.cache.vessels[slug] = { id, cached_at: new Date().toISOString() };
71
+ save(config);
72
+ },
73
+ getCachedVesselId(slug) {
74
+ const config = load();
75
+ return config.cache?.vessels?.[slug]?.id ?? null;
76
+ },
77
+ };
78
+ //# sourceMappingURL=store.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"store.js","sourceRoot":"","sources":["../../src/config/store.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,3 @@
1
+ #!/usr/bin/env node
2
+ export {};
3
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":""}
package/dist/index.js ADDED
@@ -0,0 +1,23 @@
1
+ #!/usr/bin/env node
2
+ import { Command } from 'commander';
3
+ import { registerAuthCommands } from './commands/auth.js';
4
+ import { registerVesselCommands } from './commands/vessel.js';
5
+ import { registerCargoCommands } from './commands/cargo.js';
6
+ import { registerConfigCommands } from './commands/config.js';
7
+ import { registerWhoamiCommand } from './commands/whoami.js';
8
+ const program = new Command();
9
+ program
10
+ .name('doque')
11
+ .description('Doque CLI - Manage your projects and tasks from the terminal')
12
+ .version('0.1.0')
13
+ .option('-s, --server <url>', 'API 서버 URL')
14
+ .option('-t, --token <token>', '인증 토큰 직접 지정')
15
+ .option('-n, --namespace <slug>', 'Namespace 지정')
16
+ .option('-f, --format <format>', '출력 형식 (table|json)', 'table');
17
+ registerAuthCommands(program);
18
+ registerVesselCommands(program);
19
+ registerCargoCommands(program);
20
+ registerConfigCommands(program);
21
+ registerWhoamiCommand(program);
22
+ program.parse();
23
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":";AAEA,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AACpC,OAAO,EAAE,oBAAoB,EAAE,MAAM,oBAAoB,CAAC;AAC1D,OAAO,EAAE,sBAAsB,EAAE,MAAM,sBAAsB,CAAC;AAC9D,OAAO,EAAE,qBAAqB,EAAE,MAAM,qBAAqB,CAAC;AAC5D,OAAO,EAAE,sBAAsB,EAAE,MAAM,sBAAsB,CAAC;AAC9D,OAAO,EAAE,qBAAqB,EAAE,MAAM,sBAAsB,CAAC;AAE7D,MAAM,OAAO,GAAG,IAAI,OAAO,EAAE,CAAC;AAE9B,OAAO;KACJ,IAAI,CAAC,OAAO,CAAC;KACb,WAAW,CAAC,8DAA8D,CAAC;KAC3E,OAAO,CAAC,OAAO,CAAC;KAChB,MAAM,CAAC,oBAAoB,EAAE,YAAY,CAAC;KAC1C,MAAM,CAAC,qBAAqB,EAAE,aAAa,CAAC;KAC5C,MAAM,CAAC,wBAAwB,EAAE,cAAc,CAAC;KAChD,MAAM,CAAC,uBAAuB,EAAE,oBAAoB,EAAE,OAAO,CAAC,CAAC;AAElE,oBAAoB,CAAC,OAAO,CAAC,CAAC;AAC9B,sBAAsB,CAAC,OAAO,CAAC,CAAC;AAChC,qBAAqB,CAAC,OAAO,CAAC,CAAC;AAC/B,sBAAsB,CAAC,OAAO,CAAC,CAAC;AAChC,qBAAqB,CAAC,OAAO,CAAC,CAAC;AAE/B,OAAO,CAAC,KAAK,EAAE,CAAC"}
@@ -0,0 +1,4 @@
1
+ export declare function formatTable(data: Record<string, unknown>[], columns: string[]): void;
2
+ export declare function formatJson(data: unknown): void;
3
+ export declare function formatDetail(label: string, pairs: [string, unknown][]): void;
4
+ //# sourceMappingURL=output.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"output.d.ts","sourceRoot":"","sources":["../../src/utils/output.ts"],"names":[],"mappings":"AAGA,wBAAgB,WAAW,CACzB,IAAI,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EAAE,EAC/B,OAAO,EAAE,MAAM,EAAE,GAChB,IAAI,CAsBN;AAED,wBAAgB,UAAU,CAAC,IAAI,EAAE,OAAO,GAAG,IAAI,CAE9C;AAED,wBAAgB,YAAY,CAAC,KAAK,EAAE,MAAM,EAAE,KAAK,EAAE,CAAC,MAAM,EAAE,OAAO,CAAC,EAAE,GAAG,IAAI,CAO5E"}
@@ -0,0 +1,33 @@
1
+ import chalk from 'chalk';
2
+ import Table from 'cli-table3';
3
+ export function formatTable(data, columns) {
4
+ if (data.length === 0) {
5
+ console.log(chalk.gray('(결과 없음)'));
6
+ return;
7
+ }
8
+ const table = new Table({
9
+ head: columns.map((c) => chalk.bold.cyan(c.toUpperCase())),
10
+ style: { head: [], border: [] },
11
+ });
12
+ for (const row of data) {
13
+ table.push(columns.map((col) => {
14
+ const val = row[col];
15
+ if (val == null)
16
+ return chalk.gray('-');
17
+ return String(val);
18
+ }));
19
+ }
20
+ console.log(table.toString());
21
+ }
22
+ export function formatJson(data) {
23
+ console.log(JSON.stringify(data, null, 2));
24
+ }
25
+ export function formatDetail(label, pairs) {
26
+ console.log(chalk.bold(label));
27
+ for (const [key, value] of pairs) {
28
+ if (value != null && value !== '') {
29
+ console.log(` ${chalk.cyan(key)}: ${String(value)}`);
30
+ }
31
+ }
32
+ }
33
+ //# sourceMappingURL=output.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"output.js","sourceRoot":"","sources":["../../src/utils/output.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,MAAM,OAAO,CAAC;AAC1B,OAAO,KAAK,MAAM,YAAY,CAAC;AAE/B,MAAM,UAAU,WAAW,CACzB,IAA+B,EAC/B,OAAiB;IAEjB,IAAI,IAAI,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;QACtB,OAAO,CAAC,GAAG,CAAC,KAAK,CAAC,IAAI,CAAC,SAAS,CAAC,CAAC,CAAC;QACnC,OAAO;IACT,CAAC;IAED,MAAM,KAAK,GAAG,IAAI,KAAK,CAAC;QACtB,IAAI,EAAE,OAAO,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,KAAK,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC,CAAC,WAAW,EAAE,CAAC,CAAC;QAC1D,KAAK,EAAE,EAAE,IAAI,EAAE,EAAE,EAAE,MAAM,EAAE,EAAE,EAAE;KAChC,CAAC,CAAC;IAEH,KAAK,MAAM,GAAG,IAAI,IAAI,EAAE,CAAC;QACvB,KAAK,CAAC,IAAI,CACR,OAAO,CAAC,GAAG,CAAC,CAAC,GAAG,EAAE,EAAE;YAClB,MAAM,GAAG,GAAG,GAAG,CAAC,GAAG,CAAC,CAAC;YACrB,IAAI,GAAG,IAAI,IAAI;gBAAE,OAAO,KAAK,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC;YACxC,OAAO,MAAM,CAAC,GAAG,CAAC,CAAC;QACrB,CAAC,CAAC,CACH,CAAC;IACJ,CAAC;IAED,OAAO,CAAC,GAAG,CAAC,KAAK,CAAC,QAAQ,EAAE,CAAC,CAAC;AAChC,CAAC;AAED,MAAM,UAAU,UAAU,CAAC,IAAa;IACtC,OAAO,CAAC,GAAG,CAAC,IAAI,CAAC,SAAS,CAAC,IAAI,EAAE,IAAI,EAAE,CAAC,CAAC,CAAC,CAAC;AAC7C,CAAC;AAED,MAAM,UAAU,YAAY,CAAC,KAAa,EAAE,KAA0B;IACpE,OAAO,CAAC,GAAG,CAAC,KAAK,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC,CAAC;IAC/B,KAAK,MAAM,CAAC,GAAG,EAAE,KAAK,CAAC,IAAI,KAAK,EAAE,CAAC;QACjC,IAAI,KAAK,IAAI,IAAI,IAAI,KAAK,KAAK,EAAE,EAAE,CAAC;YAClC,OAAO,CAAC,GAAG,CAAC,KAAK,KAAK,CAAC,IAAI,CAAC,GAAG,CAAC,KAAK,MAAM,CAAC,KAAK,CAAC,EAAE,CAAC,CAAC;QACxD,CAAC;IACH,CAAC;AACH,CAAC"}
package/package.json ADDED
@@ -0,0 +1,39 @@
1
+ {
2
+ "name": "doque",
3
+ "version": "0.1.0",
4
+ "description": "Doque CLI - Manage your projects and tasks from the terminal",
5
+ "type": "module",
6
+ "bin": {
7
+ "doque": "./dist/index.js"
8
+ },
9
+ "files": [
10
+ "dist"
11
+ ],
12
+ "scripts": {
13
+ "build": "tsc",
14
+ "dev": "tsc --watch",
15
+ "start": "node dist/index.js",
16
+ "prepublishOnly": "npm run build"
17
+ },
18
+ "engines": {
19
+ "node": ">=20.0.0"
20
+ },
21
+ "keywords": ["cli", "doque", "project-management", "task-management"],
22
+ "license": "MIT",
23
+ "author": "rn00n",
24
+ "repository": {
25
+ "type": "git",
26
+ "url": "https://github.com/byungryang/doque-cli"
27
+ },
28
+ "dependencies": {
29
+ "chalk": "^5.4.0",
30
+ "cli-table3": "^0.6.5",
31
+ "commander": "^13.1.0",
32
+ "open": "^10.1.0",
33
+ "ora": "^8.2.0"
34
+ },
35
+ "devDependencies": {
36
+ "@types/node": "^20.0.0",
37
+ "typescript": "^5.7.0"
38
+ }
39
+ }