harvask-cli 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 +147 -0
- package/dist/api-base.js +8 -0
- package/dist/api-base.js.map +1 -0
- package/dist/api-client.js +221 -0
- package/dist/api-client.js.map +1 -0
- package/dist/cli.js +154 -0
- package/dist/cli.js.map +1 -0
- package/dist/commands/_exec.js +63 -0
- package/dist/commands/_exec.js.map +1 -0
- package/dist/commands/api/index.js +88 -0
- package/dist/commands/api/index.js.map +1 -0
- package/dist/commands/attachment/index.js +54 -0
- package/dist/commands/attachment/index.js.map +1 -0
- package/dist/commands/auth/index.js +238 -0
- package/dist/commands/auth/index.js.map +1 -0
- package/dist/commands/calendar/index.js +167 -0
- package/dist/commands/calendar/index.js.map +1 -0
- package/dist/commands/chat/index.js +97 -0
- package/dist/commands/chat/index.js.map +1 -0
- package/dist/commands/file/index.js +42 -0
- package/dist/commands/file/index.js.map +1 -0
- package/dist/commands/global-wiki/index.js +196 -0
- package/dist/commands/global-wiki/index.js.map +1 -0
- package/dist/commands/issue/builds.js +160 -0
- package/dist/commands/issue/builds.js.map +1 -0
- package/dist/commands/issue/index.js +169 -0
- package/dist/commands/issue/index.js.map +1 -0
- package/dist/commands/issue/sub.js +149 -0
- package/dist/commands/issue/sub.js.map +1 -0
- package/dist/commands/issue/subbuilds.js +174 -0
- package/dist/commands/issue/subbuilds.js.map +1 -0
- package/dist/commands/milestone/index.js +104 -0
- package/dist/commands/milestone/index.js.map +1 -0
- package/dist/commands/note/builds.js +68 -0
- package/dist/commands/note/builds.js.map +1 -0
- package/dist/commands/note/index.js +122 -0
- package/dist/commands/note/index.js.map +1 -0
- package/dist/commands/preview-config/index.js +87 -0
- package/dist/commands/preview-config/index.js.map +1 -0
- package/dist/commands/project/builds.js +78 -0
- package/dist/commands/project/builds.js.map +1 -0
- package/dist/commands/project/index.js +103 -0
- package/dist/commands/project/index.js.map +1 -0
- package/dist/commands/scheduled-task/index.js +196 -0
- package/dist/commands/scheduled-task/index.js.map +1 -0
- package/dist/commands/site-credential/index.js +23 -0
- package/dist/commands/site-credential/index.js.map +1 -0
- package/dist/commands/skill/index.js +121 -0
- package/dist/commands/skill/index.js.map +1 -0
- package/dist/commands/storage/index.js +103 -0
- package/dist/commands/storage/index.js.map +1 -0
- package/dist/commands/user/index.js +55 -0
- package/dist/commands/user/index.js.map +1 -0
- package/dist/commands/wiki/builds.js +135 -0
- package/dist/commands/wiki/builds.js.map +1 -0
- package/dist/commands/wiki/index.js +177 -0
- package/dist/commands/wiki/index.js.map +1 -0
- package/dist/commands/workflow/index.js +111 -0
- package/dist/commands/workflow/index.js.map +1 -0
- package/dist/config.js +123 -0
- package/dist/config.js.map +1 -0
- package/dist/context.js +61 -0
- package/dist/context.js.map +1 -0
- package/dist/errors/hint-map.js +81 -0
- package/dist/errors/hint-map.js.map +1 -0
- package/dist/errors/index.js +132 -0
- package/dist/errors/index.js.map +1 -0
- package/dist/index.js +17 -0
- package/dist/index.js.map +1 -0
- package/dist/output/format.js +108 -0
- package/dist/output/format.js.map +1 -0
- package/dist/output/index.js +3 -0
- package/dist/output/index.js.map +1 -0
- package/dist/output/tty.js +22 -0
- package/dist/output/tty.js.map +1 -0
- package/dist/registry.js +47 -0
- package/dist/registry.js.map +1 -0
- package/dist/token-store.js +92 -0
- package/dist/token-store.js.map +1 -0
- package/dist/types/api.js +8 -0
- package/dist/types/api.js.map +1 -0
- package/dist/utils/bulk.js +29 -0
- package/dist/utils/bulk.js.map +1 -0
- package/dist/utils/download.js +36 -0
- package/dist/utils/download.js.map +1 -0
- package/dist/utils/file-utils.js +58 -0
- package/dist/utils/file-utils.js.map +1 -0
- package/dist/utils/input.js +66 -0
- package/dist/utils/input.js.map +1 -0
- package/dist/utils/issue-resolver.js +20 -0
- package/dist/utils/issue-resolver.js.map +1 -0
- package/dist/utils/loopback.js +86 -0
- package/dist/utils/loopback.js.map +1 -0
- package/dist/utils/pkce.js +20 -0
- package/dist/utils/pkce.js.map +1 -0
- package/dist/utils/project-resolver.js +63 -0
- package/dist/utils/project-resolver.js.map +1 -0
- package/dist/utils/prompt.js +17 -0
- package/dist/utils/prompt.js.map +1 -0
- package/dist/utils/upload.js +32 -0
- package/dist/utils/upload.js.map +1 -0
- package/package.json +44 -0
- package/skills/harvask/AGENTS.md +131 -0
- package/skills/harvask/GEMINI.md +131 -0
- package/skills/harvask/SKILL.md +134 -0
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 wown
|
|
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,147 @@
|
|
|
1
|
+
# harvask CLI
|
|
2
|
+
|
|
3
|
+
A command-line client for a **harvask tenant**, built for coding agents.
|
|
4
|
+
It covers issues, wiki, projects, notes, milestones, calendar, files, storage,
|
|
5
|
+
chat, workflows, scheduled tasks, and more. Output is JSON by default; errors
|
|
6
|
+
are structured as `Error / Why / Hint`.
|
|
7
|
+
|
|
8
|
+
This CLI targets **tenant** functionality only (central is out of scope).
|
|
9
|
+
|
|
10
|
+
## Install
|
|
11
|
+
|
|
12
|
+
```bash
|
|
13
|
+
npm install -g harvask-cli # exposes the `harvask` command globally
|
|
14
|
+
# or run without installing:
|
|
15
|
+
npx harvask-cli --help
|
|
16
|
+
```
|
|
17
|
+
|
|
18
|
+
### From source (development)
|
|
19
|
+
|
|
20
|
+
```bash
|
|
21
|
+
npm install
|
|
22
|
+
npm run build # compiles to dist/, makes dist/index.js executable
|
|
23
|
+
npm link # optional: exposes `harvask` globally for local dev
|
|
24
|
+
```
|
|
25
|
+
|
|
26
|
+
Requires Node.js >= 18.
|
|
27
|
+
|
|
28
|
+
## Authenticate
|
|
29
|
+
|
|
30
|
+
Login is **browser-based** (no password is typed into the CLI):
|
|
31
|
+
|
|
32
|
+
```bash
|
|
33
|
+
harvask login --tenant acme.harvask.app
|
|
34
|
+
# → opens your browser to approve, then receives the token automatically (loopback)
|
|
35
|
+
|
|
36
|
+
harvask login --tenant acme.harvask.app --no-browser
|
|
37
|
+
# → prints a URL; approve in any browser and paste the shown code (remote/headless)
|
|
38
|
+
```
|
|
39
|
+
|
|
40
|
+
Flow: the CLI opens your browser for approval (PKCE-protected); you approve while
|
|
41
|
+
logged into the web app, and the CLI receives a one-time code and exchanges it
|
|
42
|
+
for a token. The token is stored per-tenant in your OS config dir
|
|
43
|
+
(`tokens.json`, mode `0600`) and never appears in the browser URL. Verify with
|
|
44
|
+
`harvask whoami`; `harvask logout` revokes and removes it.
|
|
45
|
+
|
|
46
|
+
### Tenant resolution
|
|
47
|
+
|
|
48
|
+
The connection is determined by the tenant domain, so the API URL's host must
|
|
49
|
+
equal the tenant domain. In production, `--tenant` is enough.
|
|
50
|
+
|
|
51
|
+
For **local development**, map the tenant domain to your dev server in
|
|
52
|
+
`/etc/hosts` and point `--api-url` at that host:
|
|
53
|
+
|
|
54
|
+
```
|
|
55
|
+
# /etc/hosts
|
|
56
|
+
127.0.0.1 acme.harvask.test
|
|
57
|
+
```
|
|
58
|
+
```bash
|
|
59
|
+
harvask login --tenant acme.harvask.test --api-url http://acme.harvask.test:8080/api
|
|
60
|
+
```
|
|
61
|
+
|
|
62
|
+
The API URL must be `https` except for `localhost` / `127.0.0.1` /
|
|
63
|
+
`*.harvask.test` / `*.localhost` (dev allowlist).
|
|
64
|
+
|
|
65
|
+
## Usage
|
|
66
|
+
|
|
67
|
+
```bash
|
|
68
|
+
harvask project list
|
|
69
|
+
harvask issue search -p PRJ "login bug"
|
|
70
|
+
harvask issue show -p PRJ 1234 --comments
|
|
71
|
+
harvask issue create -p PRJ --title "Fix login" --type bug --priority high
|
|
72
|
+
harvask issue status -p PRJ 1234 in_progress
|
|
73
|
+
harvask issue comment create -p PRJ 1234 --content "done"
|
|
74
|
+
harvask wiki create -p PRJ --title "Docs" --content @docs.md
|
|
75
|
+
harvask note create --content "remember this" --pinned
|
|
76
|
+
harvask milestone list -p PRJ
|
|
77
|
+
harvask calendar list --from 2026-06-01 --to 2026-06-30
|
|
78
|
+
harvask user search alice
|
|
79
|
+
harvask file search -p PRJ "screenshot"
|
|
80
|
+
harvask storage upload -p PRJ --file ./report.pdf
|
|
81
|
+
harvask api GET projects # escape hatch
|
|
82
|
+
```
|
|
83
|
+
|
|
84
|
+
Command groups: `project`, `issue` (+ `comment`/`todo`/`memory`/`dependency`/
|
|
85
|
+
`bulk-create`/`bulk-update`), `wiki`, `global-wiki`, `note`, `milestone`,
|
|
86
|
+
`calendar`, `user`, `workflow`, `scheduled-task`, `chat`, `file`, `storage`,
|
|
87
|
+
`attachment`, `site-credential`, `preview-config`, `skill`, plus
|
|
88
|
+
`login`/`logout`/`whoami` and the `api` escape hatch.
|
|
89
|
+
|
|
90
|
+
- `-p, --project <key|id>` accepts a project key (`PRJ`) or numeric id.
|
|
91
|
+
- `--content` accepts a literal string, `@file`, or `-` (stdin).
|
|
92
|
+
- Global flags: `--json` (machine-readable), `--data` (payload only),
|
|
93
|
+
`--raw` (unformatted), `--profile <tenant>`, `--no-color`.
|
|
94
|
+
|
|
95
|
+
Run `harvask --help` and `harvask <command> --help` for the full reference.
|
|
96
|
+
|
|
97
|
+
## Output & exit codes
|
|
98
|
+
|
|
99
|
+
- Default output is pretty JSON on stdout. Errors print `Error / Why / Hint` to
|
|
100
|
+
stderr (or JSON with `--json`).
|
|
101
|
+
- Exit codes: `1` validation / 4xx, `2` network / 5xx, `3` auth (401/403).
|
|
102
|
+
|
|
103
|
+
## Environment variables
|
|
104
|
+
|
|
105
|
+
These override stored config (mainly for CI):
|
|
106
|
+
|
|
107
|
+
- `HARVASK_TOKEN`, `HARVASK_API_URL`, `HARVASK_TENANT`
|
|
108
|
+
- `HARVASK_TIMEOUT`, `HARVASK_RETRY_ATTEMPTS`, `HARVASK_RETRY_DELAY`
|
|
109
|
+
- `NO_COLOR` / `FORCE_COLOR`
|
|
110
|
+
|
|
111
|
+
## Develop
|
|
112
|
+
|
|
113
|
+
```bash
|
|
114
|
+
npm run build # tsc → dist/
|
|
115
|
+
npm test # build + node:test (pure build*Call / config / errors / output / token-store)
|
|
116
|
+
```
|
|
117
|
+
|
|
118
|
+
Each command is split into three parts: a pure `buildXxxCall()` (args →
|
|
119
|
+
`{method, path, query, body}`), execution via the shared API client, and
|
|
120
|
+
`printOutput()`. Tests focus on the pure functions. New categories are added by
|
|
121
|
+
implementing a `CategoryModule` under `src/commands/<cat>/` and adding one line
|
|
122
|
+
to `src/registry.ts`.
|
|
123
|
+
|
|
124
|
+
## Notes
|
|
125
|
+
|
|
126
|
+
- Notes and calendar are personal to the logged-in human user. The CLI acts as
|
|
127
|
+
the human user (not an AI user).
|
|
128
|
+
- `personal-wiki` is intentionally not exposed by the CLI.
|
|
129
|
+
- Token files use mode `0600` (POSIX). Windows is not a primary target.
|
|
130
|
+
|
|
131
|
+
## Agent skill
|
|
132
|
+
|
|
133
|
+
`skills/harvask/SKILL.md` documents the discover → fetch → act workflow for
|
|
134
|
+
coding agents and is shipped with the package. `npm link` only installs the
|
|
135
|
+
`harvask` command — to make Claude Code discover the skill, install it:
|
|
136
|
+
|
|
137
|
+
```bash
|
|
138
|
+
harvask skill install # symlink into ~/.claude/skills/harvask
|
|
139
|
+
harvask skill install --project # into ./.claude/skills/harvask
|
|
140
|
+
harvask skill install --copy # copy instead of symlink
|
|
141
|
+
harvask skill uninstall
|
|
142
|
+
harvask skill path # show source and install targets
|
|
143
|
+
```
|
|
144
|
+
|
|
145
|
+
## License
|
|
146
|
+
|
|
147
|
+
MIT
|
package/dist/api-base.js
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"api-base.js","sourceRoot":"","sources":["../src/api-base.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AACH,MAAM,CAAC,MAAM,MAAM,GAAG,KAAK,CAAC"}
|
|
@@ -0,0 +1,221 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* harvask API Client
|
|
3
|
+
*
|
|
4
|
+
* テナント API (既定の名前空間 /v1。/ai-worker は後方互換エイリアス) への HTTP クライアント。
|
|
5
|
+
* project-management-tool の kairon MCP (mcp-servers/kairon/src/api-client.ts) から流用し、
|
|
6
|
+
* 以下を変更:
|
|
7
|
+
* - クラス名 KaironApiClient -> HarvaskApiClient
|
|
8
|
+
* - 認証ヘッダは Authorization: Bearer のみ(X-AI-User-Id / X-Chat-Id は付けない)。
|
|
9
|
+
* これにより operator_user_id == ai_user_id == トークン所有者(人間)となり、
|
|
10
|
+
* notes / personal-wiki / calendar など個人スコープ API も正しく動作する。
|
|
11
|
+
* - tenantDomain が指定された場合は Host ヘッダを明示付与(開発時の Host 偽装用)。
|
|
12
|
+
* - 設定は HarvaskConfig 経由(環境変数の直接参照を廃止)。
|
|
13
|
+
*/
|
|
14
|
+
export class HarvaskApiClient {
|
|
15
|
+
config;
|
|
16
|
+
constructor(config) {
|
|
17
|
+
this.config = config;
|
|
18
|
+
}
|
|
19
|
+
/**
|
|
20
|
+
* 認証ヘッダーを生成
|
|
21
|
+
*
|
|
22
|
+
* 注: テナント解決はサーバ側で Host ヘッダにより行われるが、Node の fetch(undici) は
|
|
23
|
+
* 手動の Host ヘッダを無視し URL のホストを使う。したがって apiUrl のホストが
|
|
24
|
+
* テナントドメインと一致している必要がある(本番は https://{tenant}/api、
|
|
25
|
+
* 開発は /etc/hosts で {tenant} を 127.0.0.1 に向けて http://{tenant}:port/api)。
|
|
26
|
+
*/
|
|
27
|
+
getHeaders(isFormData = false) {
|
|
28
|
+
const headers = {
|
|
29
|
+
Accept: 'application/json',
|
|
30
|
+
Authorization: `Bearer ${this.config.token}`,
|
|
31
|
+
};
|
|
32
|
+
if (!isFormData) {
|
|
33
|
+
headers['Content-Type'] = 'application/json';
|
|
34
|
+
}
|
|
35
|
+
return headers;
|
|
36
|
+
}
|
|
37
|
+
/**
|
|
38
|
+
* APIリクエストを実行(リトライロジック付き)
|
|
39
|
+
*/
|
|
40
|
+
async request(method, endpoint, body, options) {
|
|
41
|
+
const url = `${this.config.apiUrl}${endpoint}`;
|
|
42
|
+
let lastError = null;
|
|
43
|
+
const requestTimeout = options?.timeout || this.config.timeout;
|
|
44
|
+
for (let attempt = 0; attempt <= this.config.retryAttempts; attempt++) {
|
|
45
|
+
try {
|
|
46
|
+
const controller = new AbortController();
|
|
47
|
+
const timeoutId = setTimeout(() => controller.abort(), requestTimeout);
|
|
48
|
+
const response = await fetch(url, {
|
|
49
|
+
method,
|
|
50
|
+
headers: this.getHeaders(options?.isFormData),
|
|
51
|
+
body: options?.isFormData
|
|
52
|
+
? body
|
|
53
|
+
: body
|
|
54
|
+
? JSON.stringify(body)
|
|
55
|
+
: undefined,
|
|
56
|
+
signal: controller.signal,
|
|
57
|
+
});
|
|
58
|
+
clearTimeout(timeoutId);
|
|
59
|
+
if (!response.ok) {
|
|
60
|
+
const errorBody = (await response.json().catch(() => ({})));
|
|
61
|
+
const errorMessage = errorBody.message || `HTTP ${response.status}: ${response.statusText}`;
|
|
62
|
+
// 認証・認可・not found・バリデーションはリトライしない
|
|
63
|
+
if (response.status === 401 ||
|
|
64
|
+
response.status === 403 ||
|
|
65
|
+
response.status === 404 ||
|
|
66
|
+
response.status === 422) {
|
|
67
|
+
throw new ApiClientError(errorMessage, response.status, errorBody);
|
|
68
|
+
}
|
|
69
|
+
// 429(レート制限)は Retry-After に従って待機しリトライ
|
|
70
|
+
if (response.status === 429) {
|
|
71
|
+
const retryAfter = parseInt(response.headers.get('Retry-After') || '5', 10);
|
|
72
|
+
await this.sleep(retryAfter * 1000);
|
|
73
|
+
continue;
|
|
74
|
+
}
|
|
75
|
+
// 5xx はリトライ
|
|
76
|
+
if (response.status >= 500) {
|
|
77
|
+
lastError = new ApiClientError(errorMessage, response.status, errorBody);
|
|
78
|
+
await this.sleep(this.config.retryDelay * (attempt + 1));
|
|
79
|
+
continue;
|
|
80
|
+
}
|
|
81
|
+
throw new ApiClientError(errorMessage, response.status, errorBody);
|
|
82
|
+
}
|
|
83
|
+
// 204 No Content 等の空レスポンスに対応
|
|
84
|
+
const text = await response.text();
|
|
85
|
+
return (text ? JSON.parse(text) : {});
|
|
86
|
+
}
|
|
87
|
+
catch (error) {
|
|
88
|
+
if (error instanceof ApiClientError) {
|
|
89
|
+
throw error;
|
|
90
|
+
}
|
|
91
|
+
if (process.env.HARVASK_DEBUG) {
|
|
92
|
+
console.error('[harvask] fetch error:', {
|
|
93
|
+
url,
|
|
94
|
+
errorName: error instanceof Error ? error.name : 'unknown',
|
|
95
|
+
errorMessage: error instanceof Error ? error.message : String(error),
|
|
96
|
+
});
|
|
97
|
+
}
|
|
98
|
+
if (error instanceof Error && error.name === 'AbortError') {
|
|
99
|
+
lastError = new ApiClientError('Request timeout', 408);
|
|
100
|
+
}
|
|
101
|
+
else if (error instanceof Error) {
|
|
102
|
+
const causeInfo = error.cause
|
|
103
|
+
? ` (cause: ${JSON.stringify(error.cause)})`
|
|
104
|
+
: '';
|
|
105
|
+
lastError = new ApiClientError(`Network error: ${error.message}${causeInfo}`, 0);
|
|
106
|
+
}
|
|
107
|
+
else {
|
|
108
|
+
lastError = new ApiClientError('Unknown error', 0);
|
|
109
|
+
}
|
|
110
|
+
if (attempt < this.config.retryAttempts) {
|
|
111
|
+
await this.sleep(this.config.retryDelay * (attempt + 1));
|
|
112
|
+
}
|
|
113
|
+
}
|
|
114
|
+
}
|
|
115
|
+
throw lastError || new ApiClientError('Request failed after all retries', 0);
|
|
116
|
+
}
|
|
117
|
+
sleep(ms) {
|
|
118
|
+
return new Promise((resolve) => setTimeout(resolve, ms));
|
|
119
|
+
}
|
|
120
|
+
async get(endpoint) {
|
|
121
|
+
return this.request('GET', endpoint);
|
|
122
|
+
}
|
|
123
|
+
async post(endpoint, body) {
|
|
124
|
+
return this.request('POST', endpoint, body);
|
|
125
|
+
}
|
|
126
|
+
async put(endpoint, body) {
|
|
127
|
+
return this.request('PUT', endpoint, body);
|
|
128
|
+
}
|
|
129
|
+
async patch(endpoint, body) {
|
|
130
|
+
return this.request('PATCH', endpoint, body);
|
|
131
|
+
}
|
|
132
|
+
async delete(endpoint) {
|
|
133
|
+
return this.request('DELETE', endpoint);
|
|
134
|
+
}
|
|
135
|
+
/**
|
|
136
|
+
* マルチパートPOST(ファイルアップロード用)
|
|
137
|
+
*/
|
|
138
|
+
async postMultipart(endpoint, formData) {
|
|
139
|
+
return this.request('POST', endpoint, formData, { isFormData: true, timeout: 120000 });
|
|
140
|
+
}
|
|
141
|
+
/**
|
|
142
|
+
* バイナリGET(ファイルダウンロード用)。{buffer, contentType, filename} を返す。
|
|
143
|
+
*/
|
|
144
|
+
async getBlob(endpoint) {
|
|
145
|
+
const url = `${this.config.apiUrl}${endpoint}`;
|
|
146
|
+
let lastError = null;
|
|
147
|
+
for (let attempt = 0; attempt <= this.config.retryAttempts; attempt++) {
|
|
148
|
+
try {
|
|
149
|
+
const controller = new AbortController();
|
|
150
|
+
const timeoutId = setTimeout(() => controller.abort(), 120000);
|
|
151
|
+
const headers = {
|
|
152
|
+
Authorization: `Bearer ${this.config.token}`,
|
|
153
|
+
};
|
|
154
|
+
const response = await fetch(url, { method: 'GET', headers, signal: controller.signal });
|
|
155
|
+
clearTimeout(timeoutId);
|
|
156
|
+
if (!response.ok) {
|
|
157
|
+
const errorBody = (await response.json().catch(() => ({})));
|
|
158
|
+
const errorMessage = errorBody.message || `HTTP ${response.status}: ${response.statusText}`;
|
|
159
|
+
if ([401, 403, 404, 422].includes(response.status)) {
|
|
160
|
+
throw new ApiClientError(errorMessage, response.status, errorBody);
|
|
161
|
+
}
|
|
162
|
+
if (response.status >= 500) {
|
|
163
|
+
lastError = new ApiClientError(errorMessage, response.status, errorBody);
|
|
164
|
+
await this.sleep(this.config.retryDelay * (attempt + 1));
|
|
165
|
+
continue;
|
|
166
|
+
}
|
|
167
|
+
throw new ApiClientError(errorMessage, response.status, errorBody);
|
|
168
|
+
}
|
|
169
|
+
const buffer = await response.arrayBuffer();
|
|
170
|
+
const contentType = response.headers.get('Content-Type') || 'application/octet-stream';
|
|
171
|
+
let filename;
|
|
172
|
+
const disposition = response.headers.get('Content-Disposition');
|
|
173
|
+
if (disposition) {
|
|
174
|
+
const utf8 = disposition.match(/filename\*=UTF-8''(.+?)(?:;|$)/i);
|
|
175
|
+
if (utf8) {
|
|
176
|
+
filename = decodeURIComponent(utf8[1]);
|
|
177
|
+
}
|
|
178
|
+
else {
|
|
179
|
+
const m = disposition.match(/filename="?(.+?)"?(?:;|$)/);
|
|
180
|
+
if (m) {
|
|
181
|
+
filename = m[1];
|
|
182
|
+
}
|
|
183
|
+
}
|
|
184
|
+
}
|
|
185
|
+
return { buffer, contentType, filename };
|
|
186
|
+
}
|
|
187
|
+
catch (error) {
|
|
188
|
+
if (error instanceof ApiClientError) {
|
|
189
|
+
throw error;
|
|
190
|
+
}
|
|
191
|
+
if (error instanceof Error && error.name === 'AbortError') {
|
|
192
|
+
lastError = new ApiClientError('Request timeout', 408);
|
|
193
|
+
}
|
|
194
|
+
else if (error instanceof Error) {
|
|
195
|
+
lastError = new ApiClientError(`Network error: ${error.message}`, 0);
|
|
196
|
+
}
|
|
197
|
+
else {
|
|
198
|
+
lastError = new ApiClientError('Unknown error', 0);
|
|
199
|
+
}
|
|
200
|
+
if (attempt < this.config.retryAttempts) {
|
|
201
|
+
await this.sleep(this.config.retryDelay * (attempt + 1));
|
|
202
|
+
}
|
|
203
|
+
}
|
|
204
|
+
}
|
|
205
|
+
throw lastError || new ApiClientError('Request failed after all retries', 0);
|
|
206
|
+
}
|
|
207
|
+
}
|
|
208
|
+
/**
|
|
209
|
+
* APIクライアントエラー
|
|
210
|
+
*/
|
|
211
|
+
export class ApiClientError extends Error {
|
|
212
|
+
statusCode;
|
|
213
|
+
apiError;
|
|
214
|
+
constructor(message, statusCode, apiError) {
|
|
215
|
+
super(message);
|
|
216
|
+
this.name = 'ApiClientError';
|
|
217
|
+
this.statusCode = statusCode;
|
|
218
|
+
this.apiError = apiError;
|
|
219
|
+
}
|
|
220
|
+
}
|
|
221
|
+
//# sourceMappingURL=api-client.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"api-client.js","sourceRoot":"","sources":["../src/api-client.ts"],"names":[],"mappings":"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"}
|
package/dist/cli.js
ADDED
|
@@ -0,0 +1,154 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* commander プログラム構築とトップレベル実行
|
|
3
|
+
*/
|
|
4
|
+
import { Command, CommanderError, Option } from 'commander';
|
|
5
|
+
import { CATEGORIES } from './registry.js';
|
|
6
|
+
import { CommandContext } from './context.js';
|
|
7
|
+
import { printError, exitCodeFor } from './errors/index.js';
|
|
8
|
+
export const VERSION = '0.1.0';
|
|
9
|
+
function isCommanderExit(err) {
|
|
10
|
+
return (err instanceof CommanderError ||
|
|
11
|
+
(typeof err === 'object' &&
|
|
12
|
+
err !== null &&
|
|
13
|
+
'code' in err &&
|
|
14
|
+
String(err.code).startsWith('commander.')));
|
|
15
|
+
}
|
|
16
|
+
/**
|
|
17
|
+
* グローバル出力オプション(flag, 説明)の定義。
|
|
18
|
+
*
|
|
19
|
+
* ルートだけでなく各 leaf コマンドにも同じオプションを付けることで、
|
|
20
|
+
* `harvask --json issue list`(前置)と `harvask issue list --json`(後置)の
|
|
21
|
+
* 両方が動作する(後者はコーディングエージェントの自然な書き方)。
|
|
22
|
+
*/
|
|
23
|
+
const OUTPUT_OPTIONS = [
|
|
24
|
+
['--json', 'force machine-readable JSON output (no color)'],
|
|
25
|
+
['--raw', 'print the raw API response without formatting'],
|
|
26
|
+
['--data', 'print only the primary data payload (unwrap the response envelope)'],
|
|
27
|
+
['--profile <tenant>', 'select a stored tenant profile (domain)'],
|
|
28
|
+
['--no-color', 'disable ANSI color'],
|
|
29
|
+
];
|
|
30
|
+
function addOutputOptions(cmd) {
|
|
31
|
+
for (const [flag, desc] of OUTPUT_OPTIONS) {
|
|
32
|
+
cmd.option(flag, desc);
|
|
33
|
+
}
|
|
34
|
+
return cmd;
|
|
35
|
+
}
|
|
36
|
+
/**
|
|
37
|
+
* leaf コマンドに出力オプションを付与する。ただし、コマンドが既に持つオプションと
|
|
38
|
+
* long 名または属性名が衝突するものはスキップする。
|
|
39
|
+
* 例: calendar の `--event-color`(属性 eventColor)とは衝突しないが、仮に属性 `color` を
|
|
40
|
+
* 持つコマンドがあれば `--no-color`(属性 color)を上書きしてしまうため除外する。
|
|
41
|
+
*/
|
|
42
|
+
function addOutputOptionsToLeaf(cmd) {
|
|
43
|
+
for (const [flag, desc] of OUTPUT_OPTIONS) {
|
|
44
|
+
const probe = new Option(flag);
|
|
45
|
+
const clashes = cmd.options.some((o) => o.attributeName() === probe.attributeName() || o.long === probe.long);
|
|
46
|
+
if (!clashes) {
|
|
47
|
+
cmd.option(flag, desc);
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
/**
|
|
52
|
+
* leaf コマンド(サブコマンドを持たない実行コマンド)すべてに出力オプションを付与する。
|
|
53
|
+
*/
|
|
54
|
+
function applyOutputOptionsToLeaves(cmd) {
|
|
55
|
+
for (const sub of cmd.commands) {
|
|
56
|
+
if (sub.commands.length === 0) {
|
|
57
|
+
addOutputOptionsToLeaf(sub);
|
|
58
|
+
}
|
|
59
|
+
else {
|
|
60
|
+
applyOutputOptionsToLeaves(sub);
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
/**
|
|
65
|
+
* 実行されたコマンドからルートまで親チェーンを辿り、グローバルフラグを解決する。
|
|
66
|
+
* オプションがチェーンのどの位置に現れても拾えるようにする(前置・後置どちらも可)。
|
|
67
|
+
* - json/raw/data: いずれかの階層で true なら true
|
|
68
|
+
* - color: いずれかの階層で --no-color(color===false)が指定されたら false
|
|
69
|
+
* - profile: leaf 側を優先(チェーン末尾が後勝ち)
|
|
70
|
+
*/
|
|
71
|
+
export function collectGlobalFlags(cmd) {
|
|
72
|
+
const chain = [];
|
|
73
|
+
for (let c = cmd; c; c = c.parent) {
|
|
74
|
+
chain.unshift(c);
|
|
75
|
+
}
|
|
76
|
+
let json = false;
|
|
77
|
+
let raw = false;
|
|
78
|
+
let data = false;
|
|
79
|
+
let color = true;
|
|
80
|
+
let profile;
|
|
81
|
+
for (const c of chain) {
|
|
82
|
+
const o = c.opts();
|
|
83
|
+
if (o.json)
|
|
84
|
+
json = true;
|
|
85
|
+
if (o.raw)
|
|
86
|
+
raw = true;
|
|
87
|
+
if (o.data)
|
|
88
|
+
data = true;
|
|
89
|
+
if (o.color === false)
|
|
90
|
+
color = false;
|
|
91
|
+
if (typeof o.profile === 'string')
|
|
92
|
+
profile = o.profile;
|
|
93
|
+
}
|
|
94
|
+
return { json, raw, data, color, profile };
|
|
95
|
+
}
|
|
96
|
+
function addRootHelp(program) {
|
|
97
|
+
program.addHelpText('after', `
|
|
98
|
+
Workflow (for coding agents):
|
|
99
|
+
1. discover : harvask project list / harvask issue search -p PRJ "login bug"
|
|
100
|
+
2. fetch : harvask issue show -p PRJ PRJ-12 --comments
|
|
101
|
+
3. act : harvask issue create|update|status|assign ...
|
|
102
|
+
|
|
103
|
+
Auth:
|
|
104
|
+
Run \`harvask login --tenant <domain>\` first. Tokens are stored per-tenant.
|
|
105
|
+
|
|
106
|
+
Output:
|
|
107
|
+
Default output is JSON. Use --data to print only the payload, --raw for the
|
|
108
|
+
unformatted response. Output flags work before or after the subcommand.
|
|
109
|
+
Errors go to stderr as "Error / Why / Hint".
|
|
110
|
+
|
|
111
|
+
Escape hatch:
|
|
112
|
+
harvask api <method> <path> [json] # call any /v1/* endpoint directly
|
|
113
|
+
`);
|
|
114
|
+
}
|
|
115
|
+
/**
|
|
116
|
+
* commander プログラムを構築する(パースは行わない)。テストからも利用する。
|
|
117
|
+
*/
|
|
118
|
+
export function buildProgram(ctx) {
|
|
119
|
+
const program = new Command('harvask')
|
|
120
|
+
.description('harvask tenant CLI for coding agents — a command-line client for a harvask tenant')
|
|
121
|
+
.version(VERSION)
|
|
122
|
+
.enablePositionalOptions()
|
|
123
|
+
.showHelpAfterError('(add --help for usage)');
|
|
124
|
+
addOutputOptions(program);
|
|
125
|
+
// グローバルフラグを ctx に反映(各コマンド実行前)。実行コマンドから親チェーンを辿る。
|
|
126
|
+
program.hook('preAction', (_thisCommand, actionCommand) => {
|
|
127
|
+
ctx.flags = collectGlobalFlags(actionCommand);
|
|
128
|
+
});
|
|
129
|
+
for (const category of CATEGORIES) {
|
|
130
|
+
category.register(program, ctx);
|
|
131
|
+
}
|
|
132
|
+
// 全 leaf コマンドにも出力オプションを付与(後置フラグ対応)。
|
|
133
|
+
applyOutputOptionsToLeaves(program);
|
|
134
|
+
addRootHelp(program);
|
|
135
|
+
return program;
|
|
136
|
+
}
|
|
137
|
+
export async function run(argv) {
|
|
138
|
+
const ctx = new CommandContext();
|
|
139
|
+
const program = buildProgram(ctx);
|
|
140
|
+
program.exitOverride();
|
|
141
|
+
try {
|
|
142
|
+
await program.parseAsync(argv);
|
|
143
|
+
}
|
|
144
|
+
catch (err) {
|
|
145
|
+
if (isCommanderExit(err)) {
|
|
146
|
+
// help / version / 引数エラーは commander が既に出力済み
|
|
147
|
+
process.exitCode = err.exitCode ?? 0;
|
|
148
|
+
return;
|
|
149
|
+
}
|
|
150
|
+
printError(err, ctx.out);
|
|
151
|
+
process.exitCode = exitCodeFor(err);
|
|
152
|
+
}
|
|
153
|
+
}
|
|
154
|
+
//# 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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"}
|
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* コマンド実行ヘルパ
|
|
3
|
+
*
|
|
4
|
+
* buildXxxCall(純粋関数)が返す ApiCall を、project 解決・クエリ付与した上で実行する。
|
|
5
|
+
* buildPath は純粋関数(テスト対象)。
|
|
6
|
+
*/
|
|
7
|
+
import { resolveProjectId } from '../utils/project-resolver.js';
|
|
8
|
+
/**
|
|
9
|
+
* 位置引数の文字列を ProjectArg に正規化する(数値=id, それ以外=key)。
|
|
10
|
+
*/
|
|
11
|
+
export function parseProjectArg(value) {
|
|
12
|
+
if (value === undefined || value === '') {
|
|
13
|
+
return undefined;
|
|
14
|
+
}
|
|
15
|
+
if (/^\d+$/.test(value)) {
|
|
16
|
+
return { id: parseInt(value, 10) };
|
|
17
|
+
}
|
|
18
|
+
return { key: value };
|
|
19
|
+
}
|
|
20
|
+
/**
|
|
21
|
+
* ApiCall から実際のパス({project} 置換 + クエリ付与)を構築する純粋関数。
|
|
22
|
+
*/
|
|
23
|
+
export function buildPath(call, resolvedProjectId) {
|
|
24
|
+
let path = call.path;
|
|
25
|
+
if (path.includes('{project}')) {
|
|
26
|
+
if (resolvedProjectId === undefined) {
|
|
27
|
+
throw new Error('internal: project id not resolved for path with {project}');
|
|
28
|
+
}
|
|
29
|
+
path = path.replace('{project}', String(resolvedProjectId));
|
|
30
|
+
}
|
|
31
|
+
if (call.query && Object.keys(call.query).length > 0) {
|
|
32
|
+
const qs = new URLSearchParams(call.query).toString();
|
|
33
|
+
path += (path.includes('?') ? '&' : '?') + qs;
|
|
34
|
+
}
|
|
35
|
+
return path;
|
|
36
|
+
}
|
|
37
|
+
async function callApi(client, call, path) {
|
|
38
|
+
switch (call.method) {
|
|
39
|
+
case 'GET':
|
|
40
|
+
return client.get(path);
|
|
41
|
+
case 'POST':
|
|
42
|
+
return client.post(path, call.body);
|
|
43
|
+
case 'PUT':
|
|
44
|
+
return client.put(path, call.body);
|
|
45
|
+
case 'PATCH':
|
|
46
|
+
return client.patch(path, call.body);
|
|
47
|
+
case 'DELETE':
|
|
48
|
+
return client.delete(path);
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
/**
|
|
52
|
+
* ApiCall を実行する。path に {project} を含む場合は project を解決してから実行する。
|
|
53
|
+
*/
|
|
54
|
+
export async function execCall(ctx, call, projectArg) {
|
|
55
|
+
const client = ctx.getApiClient();
|
|
56
|
+
let projectId;
|
|
57
|
+
if (call.path.includes('{project}')) {
|
|
58
|
+
projectId = await resolveProjectId(client, projectArg ?? {});
|
|
59
|
+
}
|
|
60
|
+
const path = buildPath(call, projectId);
|
|
61
|
+
return callApi(client, call, path);
|
|
62
|
+
}
|
|
63
|
+
//# sourceMappingURL=_exec.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"_exec.js","sourceRoot":"","sources":["../../src/commands/_exec.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAKH,OAAO,EAAE,gBAAgB,EAAmB,MAAM,8BAA8B,CAAC;AAEjF;;GAEG;AACH,MAAM,UAAU,eAAe,CAAC,KAAc;IAC5C,IAAI,KAAK,KAAK,SAAS,IAAI,KAAK,KAAK,EAAE,EAAE,CAAC;QACxC,OAAO,SAAS,CAAC;IACnB,CAAC;IACD,IAAI,OAAO,CAAC,IAAI,CAAC,KAAK,CAAC,EAAE,CAAC;QACxB,OAAO,EAAE,EAAE,EAAE,QAAQ,CAAC,KAAK,EAAE,EAAE,CAAC,EAAE,CAAC;IACrC,CAAC;IACD,OAAO,EAAE,GAAG,EAAE,KAAK,EAAE,CAAC;AACxB,CAAC;AAED;;GAEG;AACH,MAAM,UAAU,SAAS,CAAC,IAAa,EAAE,iBAA0B;IACjE,IAAI,IAAI,GAAG,IAAI,CAAC,IAAI,CAAC;IACrB,IAAI,IAAI,CAAC,QAAQ,CAAC,WAAW,CAAC,EAAE,CAAC;QAC/B,IAAI,iBAAiB,KAAK,SAAS,EAAE,CAAC;YACpC,MAAM,IAAI,KAAK,CAAC,2DAA2D,CAAC,CAAC;QAC/E,CAAC;QACD,IAAI,GAAG,IAAI,CAAC,OAAO,CAAC,WAAW,EAAE,MAAM,CAAC,iBAAiB,CAAC,CAAC,CAAC;IAC9D,CAAC;IACD,IAAI,IAAI,CAAC,KAAK,IAAI,MAAM,CAAC,IAAI,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC;QACrD,MAAM,EAAE,GAAG,IAAI,eAAe,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC,QAAQ,EAAE,CAAC;QACtD,IAAI,IAAI,CAAC,IAAI,CAAC,QAAQ,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,GAAG,CAAC,GAAG,EAAE,CAAC;IAChD,CAAC;IACD,OAAO,IAAI,CAAC;AACd,CAAC;AAED,KAAK,UAAU,OAAO,CACpB,MAAwB,EACxB,IAAa,EACb,IAAY;IAEZ,QAAQ,IAAI,CAAC,MAAM,EAAE,CAAC;QACpB,KAAK,KAAK;YACR,OAAO,MAAM,CAAC,GAAG,CAAC,IAAI,CAAC,CAAC;QAC1B,KAAK,MAAM;YACT,OAAO,MAAM,CAAC,IAAI,CAAC,IAAI,EAAE,IAAI,CAAC,IAAI,CAAC,CAAC;QACtC,KAAK,KAAK;YACR,OAAO,MAAM,CAAC,GAAG,CAAC,IAAI,EAAE,IAAI,CAAC,IAAI,CAAC,CAAC;QACrC,KAAK,OAAO;YACV,OAAO,MAAM,CAAC,KAAK,CAAC,IAAI,EAAE,IAAI,CAAC,IAAI,CAAC,CAAC;QACvC,KAAK,QAAQ;YACX,OAAO,MAAM,CAAC,MAAM,CAAC,IAAI,CAAC,CAAC;IAC/B,CAAC;AACH,CAAC;AAED;;GAEG;AACH,MAAM,CAAC,KAAK,UAAU,QAAQ,CAC5B,GAAmB,EACnB,IAAa,EACb,UAAuB;IAEvB,MAAM,MAAM,GAAG,GAAG,CAAC,YAAY,EAAE,CAAC;IAClC,IAAI,SAA6B,CAAC;IAClC,IAAI,IAAI,CAAC,IAAI,CAAC,QAAQ,CAAC,WAAW,CAAC,EAAE,CAAC;QACpC,SAAS,GAAG,MAAM,gBAAgB,CAAC,MAAM,EAAE,UAAU,IAAI,EAAE,CAAC,CAAC;IAC/D,CAAC;IACD,MAAM,IAAI,GAAG,SAAS,CAAC,IAAI,EAAE,SAAS,CAAC,CAAC;IACxC,OAAO,OAAO,CAAC,MAAM,EAAE,IAAI,EAAE,IAAI,CAAC,CAAC;AACrC,CAAC"}
|