fieldsmith 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 +843 -0
- package/assets/fonts/DotGothic16.ttf +0 -0
- package/assets/fonts/OFL.txt +93 -0
- package/dist/cli/exit.js +37 -0
- package/dist/cli/exit.js.map +1 -0
- package/dist/cli/index.js +699 -0
- package/dist/cli/index.js.map +1 -0
- package/dist/cli/output.js +61 -0
- package/dist/cli/output.js.map +1 -0
- package/dist/cli/progress.js +83 -0
- package/dist/cli/progress.js.map +1 -0
- package/dist/cli/promptInput.js +57 -0
- package/dist/cli/promptInput.js.map +1 -0
- package/dist/cli/schema.js +140 -0
- package/dist/cli/schema.js.map +1 -0
- package/dist/config.js +149 -0
- package/dist/config.js.map +1 -0
- package/dist/icon/emojiFont.js +163 -0
- package/dist/icon/emojiFont.js.map +1 -0
- package/dist/icon/render.js +136 -0
- package/dist/icon/render.js.map +1 -0
- package/dist/kintone/client.js +157 -0
- package/dist/kintone/client.js.map +1 -0
- package/dist/kintone/deploy.js +519 -0
- package/dist/kintone/deploy.js.map +1 -0
- package/dist/kintone/oauth.js +156 -0
- package/dist/kintone/oauth.js.map +1 -0
- package/dist/kintone/tokenStore.js +87 -0
- package/dist/kintone/tokenStore.js.map +1 -0
- package/dist/llm/generate.js +317 -0
- package/dist/llm/generate.js.map +1 -0
- package/dist/llm/prompt.js +142 -0
- package/dist/llm/prompt.js.map +1 -0
- package/dist/spec/appSpec.js +283 -0
- package/dist/spec/appSpec.js.map +1 -0
- package/dist/spec/diff.js +218 -0
- package/dist/spec/diff.js.map +1 -0
- package/dist/spec/fieldSpec.js +353 -0
- package/dist/spec/fieldSpec.js.map +1 -0
- package/dist/spec/fromKintone.js +230 -0
- package/dist/spec/fromKintone.js.map +1 -0
- package/dist/spec/layout.js +215 -0
- package/dist/spec/layout.js.map +1 -0
- package/dist/spec/toKintone.js +170 -0
- package/dist/spec/toKintone.js.map +1 -0
- package/package.json +59 -0
|
@@ -0,0 +1,156 @@
|
|
|
1
|
+
import { randomBytes } from "node:crypto";
|
|
2
|
+
/**
|
|
3
|
+
* 必要な OAuth スコープ。
|
|
4
|
+
*
|
|
5
|
+
* 書き込み系 (アプリ作成・フィールド追加・一覧/一般設定の変更・運用環境への反映) は
|
|
6
|
+
* `k:app_settings:write` に収まるが、**反映状況の確認 (GET /k/v1/preview/app/deploy.json) は
|
|
7
|
+
* 読み取りなので `k:app_settings:read` が要る**。
|
|
8
|
+
* write だけだと、デプロイまで成功したあとの状況確認で 403 (CB_OA01) になる。
|
|
9
|
+
*
|
|
10
|
+
* アプリアイコンを設定する場合は画像をアップロードするので `k:file:write` も要る。
|
|
11
|
+
* レコードには触れないので、レコード系のスコープは要求しない。
|
|
12
|
+
*/
|
|
13
|
+
export const REQUIRED_SCOPES = [
|
|
14
|
+
"k:app_settings:read",
|
|
15
|
+
"k:app_settings:write",
|
|
16
|
+
// アプリアイコンの画像をアップロードする (POST /k/v1/file.json) ために要る。
|
|
17
|
+
"k:file:write",
|
|
18
|
+
];
|
|
19
|
+
/** 認可リクエストに載せるスコープ文字列 (OAuth の仕様どおり空白区切り)。 */
|
|
20
|
+
export const REQUIRED_SCOPE = REQUIRED_SCOPES.join(" ");
|
|
21
|
+
/**
|
|
22
|
+
* 付与済みのスコープのうち、足りないものを返す。
|
|
23
|
+
* kintone がトークン応答で scope を返さない場合は判定できないので空配列を返す。
|
|
24
|
+
*/
|
|
25
|
+
export function missingScopes(granted) {
|
|
26
|
+
if (granted === undefined || granted.trim() === "")
|
|
27
|
+
return [];
|
|
28
|
+
const set = new Set(granted.split(/\s+/));
|
|
29
|
+
return REQUIRED_SCOPES.filter((scope) => !set.has(scope));
|
|
30
|
+
}
|
|
31
|
+
export class OAuthError extends Error {
|
|
32
|
+
detail;
|
|
33
|
+
constructor(message, detail) {
|
|
34
|
+
super(detail === undefined ? message : `${message}\n${detail}`);
|
|
35
|
+
this.detail = detail;
|
|
36
|
+
this.name = "OAuthError";
|
|
37
|
+
}
|
|
38
|
+
}
|
|
39
|
+
/** 再ログインが必要な状態。CLI 側でこれを見て `fieldsmith login` を案内する。 */
|
|
40
|
+
export class ReauthRequiredError extends OAuthError {
|
|
41
|
+
constructor(message, detail) {
|
|
42
|
+
super(message, detail);
|
|
43
|
+
this.name = "ReauthRequiredError";
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
/** 認可 URL と、リダイレクト後に照合するための state を生成する。 */
|
|
47
|
+
export function buildAuthorizationRequest(config) {
|
|
48
|
+
const state = randomBytes(24).toString("base64url");
|
|
49
|
+
const url = new URL(config.authorizationEndpoint);
|
|
50
|
+
url.searchParams.set("client_id", config.clientId);
|
|
51
|
+
url.searchParams.set("redirect_uri", config.redirectUri);
|
|
52
|
+
url.searchParams.set("response_type", "code");
|
|
53
|
+
url.searchParams.set("scope", REQUIRED_SCOPE);
|
|
54
|
+
url.searchParams.set("state", state);
|
|
55
|
+
return { url: url.toString(), state };
|
|
56
|
+
}
|
|
57
|
+
/**
|
|
58
|
+
* リダイレクト先の URL から認可コードを取り出す。
|
|
59
|
+
* state が一致しない場合は CSRF の可能性があるので必ず中断する。
|
|
60
|
+
*/
|
|
61
|
+
export function extractAuthorizationCode(redirectedUrl, expectedState) {
|
|
62
|
+
let url;
|
|
63
|
+
try {
|
|
64
|
+
url = new URL(redirectedUrl.trim());
|
|
65
|
+
}
|
|
66
|
+
catch {
|
|
67
|
+
throw new OAuthError("リダイレクト先の URL として解釈できませんでした。ブラウザのアドレスバーの内容をそのまま貼り付けてください。");
|
|
68
|
+
}
|
|
69
|
+
const error = url.searchParams.get("error");
|
|
70
|
+
if (error !== null) {
|
|
71
|
+
throw new OAuthError("認可が拒否されました。", `error=${error} ${url.searchParams.get("error_description") ?? ""}`.trim());
|
|
72
|
+
}
|
|
73
|
+
const state = url.searchParams.get("state");
|
|
74
|
+
if (state !== expectedState) {
|
|
75
|
+
throw new OAuthError("state が一致しません。認可をやり直してください。", `期待値と異なる state が返されました (受信: ${state ?? "なし"})`);
|
|
76
|
+
}
|
|
77
|
+
const code = url.searchParams.get("code");
|
|
78
|
+
if (code === null || code === "") {
|
|
79
|
+
throw new OAuthError("URL に認可コード (code) が含まれていません。");
|
|
80
|
+
}
|
|
81
|
+
return code;
|
|
82
|
+
}
|
|
83
|
+
/** 認可コードをアクセストークン・リフレッシュトークンに交換する。 */
|
|
84
|
+
export async function exchangeAuthorizationCode(config, code, fetchImpl = fetch) {
|
|
85
|
+
const body = new URLSearchParams({
|
|
86
|
+
grant_type: "authorization_code",
|
|
87
|
+
code,
|
|
88
|
+
redirect_uri: config.redirectUri,
|
|
89
|
+
});
|
|
90
|
+
const response = await requestToken(config, body, fetchImpl);
|
|
91
|
+
if (response.refresh_token === undefined) {
|
|
92
|
+
throw new OAuthError("リフレッシュトークンが返されませんでした。OAuth クライアントの設定を確認してください。");
|
|
93
|
+
}
|
|
94
|
+
return toStoredToken(response, response.refresh_token);
|
|
95
|
+
}
|
|
96
|
+
/**
|
|
97
|
+
* リフレッシュトークンでアクセストークンを更新する。
|
|
98
|
+
* kintone のリフレッシュトークンには有効期限が無いため、通常はこの経路だけで動き続ける。
|
|
99
|
+
*/
|
|
100
|
+
export async function refreshAccessToken(config, refreshToken, fetchImpl = fetch) {
|
|
101
|
+
const body = new URLSearchParams({
|
|
102
|
+
grant_type: "refresh_token",
|
|
103
|
+
refresh_token: refreshToken,
|
|
104
|
+
});
|
|
105
|
+
let response;
|
|
106
|
+
try {
|
|
107
|
+
response = await requestToken(config, body, fetchImpl);
|
|
108
|
+
}
|
|
109
|
+
catch (error) {
|
|
110
|
+
if (error instanceof OAuthError) {
|
|
111
|
+
throw new ReauthRequiredError("アクセストークンの更新に失敗しました。`fieldsmith login` で認可をやり直してください。", error.detail);
|
|
112
|
+
}
|
|
113
|
+
throw error;
|
|
114
|
+
}
|
|
115
|
+
// リフレッシュトークンがローテーションされない実装もあるため、返らなければ元の値を使い続ける。
|
|
116
|
+
return toStoredToken(response, response.refresh_token ?? refreshToken);
|
|
117
|
+
}
|
|
118
|
+
async function requestToken(config, body, fetchImpl) {
|
|
119
|
+
const credentials = Buffer.from(`${config.clientId}:${config.clientSecret}`).toString("base64");
|
|
120
|
+
const response = await fetchImpl(config.tokenEndpoint, {
|
|
121
|
+
method: "POST",
|
|
122
|
+
headers: {
|
|
123
|
+
Authorization: `Basic ${credentials}`,
|
|
124
|
+
"Content-Type": "application/x-www-form-urlencoded",
|
|
125
|
+
Accept: "application/json",
|
|
126
|
+
},
|
|
127
|
+
body: body.toString(),
|
|
128
|
+
});
|
|
129
|
+
const text = await response.text();
|
|
130
|
+
if (!response.ok) {
|
|
131
|
+
throw new OAuthError(`トークンエンドポイントがエラーを返しました (HTTP ${response.status})`, text.slice(0, 500));
|
|
132
|
+
}
|
|
133
|
+
let parsed;
|
|
134
|
+
try {
|
|
135
|
+
parsed = JSON.parse(text);
|
|
136
|
+
}
|
|
137
|
+
catch {
|
|
138
|
+
throw new OAuthError("トークンエンドポイントの応答を JSON として解釈できませんでした", text.slice(0, 500));
|
|
139
|
+
}
|
|
140
|
+
const accessToken = parsed?.access_token;
|
|
141
|
+
if (typeof accessToken !== "string" || accessToken === "") {
|
|
142
|
+
throw new OAuthError("トークンエンドポイントの応答に access_token が含まれていません", text.slice(0, 500));
|
|
143
|
+
}
|
|
144
|
+
return parsed;
|
|
145
|
+
}
|
|
146
|
+
function toStoredToken(response, refreshToken) {
|
|
147
|
+
// kintone のアクセストークンは 1 時間。expires_in が返らない場合もその想定で扱う。
|
|
148
|
+
const expiresInSeconds = response.expires_in ?? 3600;
|
|
149
|
+
return {
|
|
150
|
+
accessToken: response.access_token,
|
|
151
|
+
refreshToken,
|
|
152
|
+
expiresAt: Date.now() + expiresInSeconds * 1000,
|
|
153
|
+
...(response.scope === undefined ? {} : { scope: response.scope }),
|
|
154
|
+
};
|
|
155
|
+
}
|
|
156
|
+
//# sourceMappingURL=oauth.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"oauth.js","sourceRoot":"","sources":["../../src/kintone/oauth.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,87 @@
|
|
|
1
|
+
import { chmodSync, mkdirSync, readFileSync, rmSync, writeFileSync } from "node:fs";
|
|
2
|
+
import { homedir } from "node:os";
|
|
3
|
+
import { dirname, join } from "node:path";
|
|
4
|
+
import { z } from "zod";
|
|
5
|
+
const storedTokenSchema = z.object({
|
|
6
|
+
accessToken: z.string().min(1),
|
|
7
|
+
refreshToken: z.string().min(1),
|
|
8
|
+
/** アクセストークンの失効時刻 (エポックミリ秒)。 */
|
|
9
|
+
expiresAt: z.number().int().nonnegative(),
|
|
10
|
+
scope: z.string().optional(),
|
|
11
|
+
});
|
|
12
|
+
/** baseUrl をキーに複数の kintone ドメインのトークンを保持する。 */
|
|
13
|
+
const tokenFileSchema = z.object({
|
|
14
|
+
version: z.literal(1),
|
|
15
|
+
tokens: z.record(z.string(), storedTokenSchema),
|
|
16
|
+
});
|
|
17
|
+
/**
|
|
18
|
+
* トークンの保存先。
|
|
19
|
+
* `FIELDSMITH_CONFIG_DIR` で上書きできるようにしてあるのは、テストと、
|
|
20
|
+
* 複数の kintone 環境を切り替えて使う場合のため。
|
|
21
|
+
*/
|
|
22
|
+
export function tokenFilePath(env = process.env) {
|
|
23
|
+
const base = env["FIELDSMITH_CONFIG_DIR"] ?? join(env["XDG_CONFIG_HOME"] ?? join(homedir(), ".config"), "fieldsmith");
|
|
24
|
+
return join(base, "tokens.json");
|
|
25
|
+
}
|
|
26
|
+
function readTokenFile(path) {
|
|
27
|
+
let content;
|
|
28
|
+
try {
|
|
29
|
+
content = readFileSync(path, "utf-8");
|
|
30
|
+
}
|
|
31
|
+
catch {
|
|
32
|
+
return { version: 1, tokens: {} };
|
|
33
|
+
}
|
|
34
|
+
const result = tokenFileSchema.safeParse(safeJsonParse(content));
|
|
35
|
+
if (!result.success) {
|
|
36
|
+
// 壊れたファイルで動けなくなるより、再ログインを促すほうが復旧が早い。
|
|
37
|
+
return { version: 1, tokens: {} };
|
|
38
|
+
}
|
|
39
|
+
return result.data;
|
|
40
|
+
}
|
|
41
|
+
export function loadToken(baseUrl, env = process.env) {
|
|
42
|
+
const file = readTokenFile(tokenFilePath(env));
|
|
43
|
+
return file.tokens[normalizeBaseUrl(baseUrl)] ?? null;
|
|
44
|
+
}
|
|
45
|
+
/** トークンを保存する。ファイルはパーミッション 0600 で作成する。 */
|
|
46
|
+
export function saveToken(baseUrl, token, env = process.env) {
|
|
47
|
+
const path = tokenFilePath(env);
|
|
48
|
+
const file = readTokenFile(path);
|
|
49
|
+
file.tokens[normalizeBaseUrl(baseUrl)] = token;
|
|
50
|
+
mkdirSync(dirname(path), { recursive: true, mode: 0o700 });
|
|
51
|
+
writeFileSync(path, `${JSON.stringify(file, null, 2)}\n`, { encoding: "utf-8", mode: 0o600 });
|
|
52
|
+
// 既存ファイルへの上書きでは writeFileSync の mode が効かないため明示的に設定する。
|
|
53
|
+
chmodSync(path, 0o600);
|
|
54
|
+
}
|
|
55
|
+
/** 保存済みトークンを破棄する。対象が無ければ false を返す。 */
|
|
56
|
+
export function clearToken(baseUrl, env = process.env) {
|
|
57
|
+
const path = tokenFilePath(env);
|
|
58
|
+
const file = readTokenFile(path);
|
|
59
|
+
const key = normalizeBaseUrl(baseUrl);
|
|
60
|
+
if (!(key in file.tokens))
|
|
61
|
+
return false;
|
|
62
|
+
delete file.tokens[key];
|
|
63
|
+
if (Object.keys(file.tokens).length === 0) {
|
|
64
|
+
rmSync(path, { force: true });
|
|
65
|
+
}
|
|
66
|
+
else {
|
|
67
|
+
writeFileSync(path, `${JSON.stringify(file, null, 2)}\n`, { encoding: "utf-8", mode: 0o600 });
|
|
68
|
+
chmodSync(path, 0o600);
|
|
69
|
+
}
|
|
70
|
+
return true;
|
|
71
|
+
}
|
|
72
|
+
/** 失効まで残り 60 秒を切っていれば期限切れとみなす (通信中の失効を避けるため)。 */
|
|
73
|
+
export function isExpired(token, now = Date.now()) {
|
|
74
|
+
return token.expiresAt - 60_000 <= now;
|
|
75
|
+
}
|
|
76
|
+
function normalizeBaseUrl(baseUrl) {
|
|
77
|
+
return baseUrl.replace(/\/+$/, "").toLowerCase();
|
|
78
|
+
}
|
|
79
|
+
function safeJsonParse(content) {
|
|
80
|
+
try {
|
|
81
|
+
return JSON.parse(content);
|
|
82
|
+
}
|
|
83
|
+
catch {
|
|
84
|
+
return null;
|
|
85
|
+
}
|
|
86
|
+
}
|
|
87
|
+
//# sourceMappingURL=tokenStore.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"tokenStore.js","sourceRoot":"","sources":["../../src/kintone/tokenStore.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;AAC1C,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AAExB,MAAM,iBAAiB,GAAG,CAAC,CAAC,MAAM,CAAC;IACjC,WAAW,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,GAAG,CAAC,CAAC,CAAC;IAC9B,YAAY,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,GAAG,CAAC,CAAC,CAAC;IAC/B,+BAA+B;IAC/B,SAAS,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,GAAG,EAAE,CAAC,WAAW,EAAE;IACzC,KAAK,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,QAAQ,EAAE;CAC7B,CAAC,CAAC;AAIH,8CAA8C;AAC9C,MAAM,eAAe,GAAG,CAAC,CAAC,MAAM,CAAC;IAC/B,OAAO,EAAE,CAAC,CAAC,OAAO,CAAC,CAAC,CAAC;IACrB,MAAM,EAAE,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,MAAM,EAAE,EAAE,iBAAiB,CAAC;CAChD,CAAC,CAAC;AAEH;;;;GAIG;AACH,MAAM,UAAU,aAAa,CAAC,MAAyB,OAAO,CAAC,GAAG;IAChE,MAAM,IAAI,GAAG,GAAG,CAAC,uBAAuB,CAAC,IAAI,IAAI,CAAC,GAAG,CAAC,iBAAiB,CAAC,IAAI,IAAI,CAAC,OAAO,EAAE,EAAE,SAAS,CAAC,EAAE,YAAY,CAAC,CAAC;IACtH,OAAO,IAAI,CAAC,IAAI,EAAE,aAAa,CAAC,CAAC;AACnC,CAAC;AAED,SAAS,aAAa,CAAC,IAAY;IACjC,IAAI,OAAe,CAAC;IACpB,IAAI,CAAC;QACH,OAAO,GAAG,YAAY,CAAC,IAAI,EAAE,OAAO,CAAC,CAAC;IACxC,CAAC;IAAC,MAAM,CAAC;QACP,OAAO,EAAE,OAAO,EAAE,CAAC,EAAE,MAAM,EAAE,EAAE,EAAE,CAAC;IACpC,CAAC;IAED,MAAM,MAAM,GAAG,eAAe,CAAC,SAAS,CAAC,aAAa,CAAC,OAAO,CAAC,CAAC,CAAC;IACjE,IAAI,CAAC,MAAM,CAAC,OAAO,EAAE,CAAC;QACpB,qCAAqC;QACrC,OAAO,EAAE,OAAO,EAAE,CAAC,EAAE,MAAM,EAAE,EAAE,EAAE,CAAC;IACpC,CAAC;IACD,OAAO,MAAM,CAAC,IAAI,CAAC;AACrB,CAAC;AAED,MAAM,UAAU,SAAS,CAAC,OAAe,EAAE,MAAyB,OAAO,CAAC,GAAG;IAC7E,MAAM,IAAI,GAAG,aAAa,CAAC,aAAa,CAAC,GAAG,CAAC,CAAC,CAAC;IAC/C,OAAO,IAAI,CAAC,MAAM,CAAC,gBAAgB,CAAC,OAAO,CAAC,CAAC,IAAI,IAAI,CAAC;AACxD,CAAC;AAED,yCAAyC;AACzC,MAAM,UAAU,SAAS,CACvB,OAAe,EACf,KAAkB,EAClB,MAAyB,OAAO,CAAC,GAAG;IAEpC,MAAM,IAAI,GAAG,aAAa,CAAC,GAAG,CAAC,CAAC;IAChC,MAAM,IAAI,GAAG,aAAa,CAAC,IAAI,CAAC,CAAC;IACjC,IAAI,CAAC,MAAM,CAAC,gBAAgB,CAAC,OAAO,CAAC,CAAC,GAAG,KAAK,CAAC;IAE/C,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,IAAI,EAAE,IAAI,EAAE,CAAC,CAAC,IAAI,EAAE,EAAE,QAAQ,EAAE,OAAO,EAAE,IAAI,EAAE,KAAK,EAAE,CAAC,CAAC;IAC9F,sDAAsD;IACtD,SAAS,CAAC,IAAI,EAAE,KAAK,CAAC,CAAC;AACzB,CAAC;AAED,uCAAuC;AACvC,MAAM,UAAU,UAAU,CAAC,OAAe,EAAE,MAAyB,OAAO,CAAC,GAAG;IAC9E,MAAM,IAAI,GAAG,aAAa,CAAC,GAAG,CAAC,CAAC;IAChC,MAAM,IAAI,GAAG,aAAa,CAAC,IAAI,CAAC,CAAC;IACjC,MAAM,GAAG,GAAG,gBAAgB,CAAC,OAAO,CAAC,CAAC;IACtC,IAAI,CAAC,CAAC,GAAG,IAAI,IAAI,CAAC,MAAM,CAAC;QAAE,OAAO,KAAK,CAAC;IAExC,OAAO,IAAI,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC;IACxB,IAAI,MAAM,CAAC,IAAI,CAAC,IAAI,CAAC,MAAM,CAAC,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;QAC1C,MAAM,CAAC,IAAI,EAAE,EAAE,KAAK,EAAE,IAAI,EAAE,CAAC,CAAC;IAChC,CAAC;SAAM,CAAC;QACN,aAAa,CAAC,IAAI,EAAE,GAAG,IAAI,CAAC,SAAS,CAAC,IAAI,EAAE,IAAI,EAAE,CAAC,CAAC,IAAI,EAAE,EAAE,QAAQ,EAAE,OAAO,EAAE,IAAI,EAAE,KAAK,EAAE,CAAC,CAAC;QAC9F,SAAS,CAAC,IAAI,EAAE,KAAK,CAAC,CAAC;IACzB,CAAC;IACD,OAAO,IAAI,CAAC;AACd,CAAC;AAED,iDAAiD;AACjD,MAAM,UAAU,SAAS,CAAC,KAAkB,EAAE,GAAG,GAAG,IAAI,CAAC,GAAG,EAAE;IAC5D,OAAO,KAAK,CAAC,SAAS,GAAG,MAAM,IAAI,GAAG,CAAC;AACzC,CAAC;AAED,SAAS,gBAAgB,CAAC,OAAe;IACvC,OAAO,OAAO,CAAC,OAAO,CAAC,MAAM,EAAE,EAAE,CAAC,CAAC,WAAW,EAAE,CAAC;AACnD,CAAC;AAED,SAAS,aAAa,CAAC,OAAe;IACpC,IAAI,CAAC;QACH,OAAO,IAAI,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC;IAC7B,CAAC;IAAC,MAAM,CAAC;QACP,OAAO,IAAI,CAAC;IACd,CAAC;AACH,CAAC"}
|
|
@@ -0,0 +1,317 @@
|
|
|
1
|
+
import Anthropic from "@anthropic-ai/sdk";
|
|
2
|
+
import { zodOutputFormat } from "@anthropic-ai/sdk/helpers/zod";
|
|
3
|
+
// SDK の zodOutputFormat は zod v4 のスキーマを要求する。
|
|
4
|
+
// zod 3.25 は v4 を `zod/v4` として同梱しているので、LLM の入出力スキーマだけこちらを使う。
|
|
5
|
+
// このスキーマの値は toAppSpecInput() でただのオブジェクトに落としてからコア (zod v3) に渡すため、
|
|
6
|
+
// 2 つのバージョンが混ざることはない。
|
|
7
|
+
import * as z from "zod/v4";
|
|
8
|
+
import { APP_THEMES, parseAppSpec } from "../spec/appSpec.js";
|
|
9
|
+
import { isOptionFieldType, SUPPORTED_FIELD_TYPES } from "../spec/fieldSpec.js";
|
|
10
|
+
import { JSON_SHAPE_INSTRUCTION, REVISE_INSTRUCTION, SYSTEM_PROMPT } from "./prompt.js";
|
|
11
|
+
/**
|
|
12
|
+
* LLM に生成させるスキーマ。
|
|
13
|
+
*
|
|
14
|
+
* コアの AppSpec は型ごとの discriminated union だが、それをそのまま JSON Schema にすると
|
|
15
|
+
* 構造化出力が受け付けない。受け取った後にコアの parseAppSpec() で厳密に検証する二段構えにしている。
|
|
16
|
+
*
|
|
17
|
+
* ここは構造化出力の 2 つの上限に挟まれているため、書き方に強い制約がある。
|
|
18
|
+
* 構造化出力は JSON Schema を制約付きデコード用の文法にコンパイルするので、
|
|
19
|
+
* 文法が膨らむ書き方はサーバー側 (Claude API) が 400 で弾く。
|
|
20
|
+
* - `.nullable()` は anyOf (union) になる
|
|
21
|
+
* → "Schemas contains too many parameters with union types (limit: 16 parameters with unions)"
|
|
22
|
+
* - `.optional()` は union にならないが、キーの出現順が自由になるぶん組み合わせが増える
|
|
23
|
+
* → "Schema is too complex"
|
|
24
|
+
*
|
|
25
|
+
* そこで **すべてのプロパティを必須にし、「指定なし」は空文字・空配列で表す**。
|
|
26
|
+
* union も optional も 0 になり、grammar が最小になる。
|
|
27
|
+
* 空の値は toAppSpecInput() で落とすので、コア側には現れない。
|
|
28
|
+
* この不変条件は tests/llm.test.ts で検証している。
|
|
29
|
+
*/
|
|
30
|
+
const llmFieldSchema = z.object({
|
|
31
|
+
type: z.enum(SUPPORTED_FIELD_TYPES),
|
|
32
|
+
label: z.string(),
|
|
33
|
+
/**
|
|
34
|
+
* フィールドコード。新規のフィールドは空文字にして、label から導かせる。
|
|
35
|
+
* **既存アプリを直すときは、既存フィールドのコードを必ずそのまま書く。**
|
|
36
|
+
* ここが変わると別のフィールドとみなされ、追加 + 元は削除候補になる。
|
|
37
|
+
*/
|
|
38
|
+
code: z.string(),
|
|
39
|
+
required: z.boolean(),
|
|
40
|
+
/** 選択肢。RADIO_BUTTON / CHECK_BOX / MULTI_SELECT / DROP_DOWN 以外は空配列。 */
|
|
41
|
+
options: z.array(z.string()),
|
|
42
|
+
/** 計算式。CALC 以外は空文字。 */
|
|
43
|
+
expression: z.string(),
|
|
44
|
+
/** 意味のまとまりの名前。フォームで横に並べる単位になる。 */
|
|
45
|
+
group: z.string(),
|
|
46
|
+
});
|
|
47
|
+
const llmViewSchema = z.object({
|
|
48
|
+
name: z.string(),
|
|
49
|
+
/** 一覧に表示するフィールド名。 */
|
|
50
|
+
fields: z.array(z.string()),
|
|
51
|
+
});
|
|
52
|
+
const llmAppSpecSchema = z.object({
|
|
53
|
+
name: z.string(),
|
|
54
|
+
/** アプリの説明。不要なら空文字。 */
|
|
55
|
+
description: z.string(),
|
|
56
|
+
theme: z.enum(APP_THEMES),
|
|
57
|
+
/** アプリアイコンにする絵文字 1 文字。思い当たらなければ空文字。 */
|
|
58
|
+
icon: z.string(),
|
|
59
|
+
/** レコードのタイトルに使うフィールド名。決められなければ空文字。 */
|
|
60
|
+
titleFieldCode: z.string(),
|
|
61
|
+
fields: z.array(llmFieldSchema),
|
|
62
|
+
/** 一覧。不要なら空配列。 */
|
|
63
|
+
views: z.array(llmViewSchema),
|
|
64
|
+
});
|
|
65
|
+
/**
|
|
66
|
+
* 構造化出力を使わない退避経路で使う、緩いスキーマ。
|
|
67
|
+
* こちらは JSON Schema として送らないので複雑さの上限とは無関係。
|
|
68
|
+
* モデルが項目を省いても既定値で埋めて、コアの検証に持ち込めるようにする。
|
|
69
|
+
*/
|
|
70
|
+
const llmAppSpecLenientSchema = z.object({
|
|
71
|
+
name: z.string(),
|
|
72
|
+
description: z.string().default(""),
|
|
73
|
+
theme: z.enum(APP_THEMES).default("WHITE"),
|
|
74
|
+
icon: z.string().default(""),
|
|
75
|
+
titleFieldCode: z.string().default(""),
|
|
76
|
+
fields: z.array(z.object({
|
|
77
|
+
type: z.enum(SUPPORTED_FIELD_TYPES),
|
|
78
|
+
label: z.string(),
|
|
79
|
+
code: z.string().default(""),
|
|
80
|
+
required: z.boolean().default(false),
|
|
81
|
+
options: z.array(z.string()).default([]),
|
|
82
|
+
expression: z.string().default(""),
|
|
83
|
+
group: z.string().default(""),
|
|
84
|
+
})),
|
|
85
|
+
views: z
|
|
86
|
+
.array(z.object({ name: z.string(), fields: z.array(z.string()).default([]) }))
|
|
87
|
+
.default([]),
|
|
88
|
+
});
|
|
89
|
+
/** スキーマを構造化出力の形式に変換したもの。上限チェックのためテストからも参照する。 */
|
|
90
|
+
export const LLM_OUTPUT_FORMAT = zodOutputFormat(llmAppSpecSchema);
|
|
91
|
+
export class AppSpecGenerationError extends Error {
|
|
92
|
+
cause;
|
|
93
|
+
constructor(message, cause) {
|
|
94
|
+
super(message);
|
|
95
|
+
this.cause = cause;
|
|
96
|
+
this.name = "AppSpecGenerationError";
|
|
97
|
+
}
|
|
98
|
+
}
|
|
99
|
+
export const DEFAULT_MODEL = "claude-opus-5";
|
|
100
|
+
/**
|
|
101
|
+
* 1 リクエストの上限時間。
|
|
102
|
+
* SDK の既定は 10 分だが、CLI がそれだけ無反応だと事故と区別が付かないので短くする。
|
|
103
|
+
*/
|
|
104
|
+
export const DEFAULT_TIMEOUT_MS = 180_000;
|
|
105
|
+
/**
|
|
106
|
+
* 自然文のプロンプトから AppSpec を生成する。
|
|
107
|
+
* LLM の出力は必ずコアのスキーマで再検証するため、この関数が返す値は
|
|
108
|
+
* そのまま deployAppSpec() に渡せる。
|
|
109
|
+
*/
|
|
110
|
+
export async function generateAppSpec(prompt, options = {}) {
|
|
111
|
+
const client = options.client ?? new Anthropic();
|
|
112
|
+
const model = options.model ?? DEFAULT_MODEL;
|
|
113
|
+
const notify = options.onEvent ?? (() => { });
|
|
114
|
+
/**
|
|
115
|
+
* 1 回のリクエストを実行する。
|
|
116
|
+
* useSchema が false のときは構造化出力を使わず、JSON を直接書かせる。
|
|
117
|
+
*/
|
|
118
|
+
const request = async (useSchema) => {
|
|
119
|
+
const stream = client.beta.messages.stream({
|
|
120
|
+
model,
|
|
121
|
+
max_tokens: 16000,
|
|
122
|
+
// display: "summarized" にしないと thinking の中身は空で流れてくる。
|
|
123
|
+
// 進捗として見せたいので明示する。
|
|
124
|
+
thinking: { type: "adaptive", display: "summarized" },
|
|
125
|
+
system: (options.base === undefined ? SYSTEM_PROMPT : SYSTEM_PROMPT + REVISE_INSTRUCTION) +
|
|
126
|
+
(useSchema ? "" : JSON_SHAPE_INSTRUCTION),
|
|
127
|
+
messages: [{ role: "user", content: buildUserMessage(prompt, options.base) }],
|
|
128
|
+
...(useSchema ? { output_config: { format: LLM_OUTPUT_FORMAT } } : {}),
|
|
129
|
+
// 安全性分類器が要求を差し戻した場合に、別モデルへ自動でフォールバックさせる。
|
|
130
|
+
betas: ["server-side-fallback-2026-07-01"],
|
|
131
|
+
fallbacks: "default",
|
|
132
|
+
}, { timeout: options.timeoutMs ?? DEFAULT_TIMEOUT_MS });
|
|
133
|
+
stream.on("connect", () => notify({ type: "connected", model, requestId: stream.request_id ?? null }));
|
|
134
|
+
stream.on("streamEvent", (event) => notify({ type: "streamEvent", name: event.type }));
|
|
135
|
+
stream.on("thinking", (delta) => notify({ type: "thinking", delta }));
|
|
136
|
+
stream.on("text", (delta, snapshot) => notify({ type: "writing", delta, totalChars: snapshot.length }));
|
|
137
|
+
return await stream.finalMessage();
|
|
138
|
+
};
|
|
139
|
+
let response;
|
|
140
|
+
let usedSchema = true;
|
|
141
|
+
try {
|
|
142
|
+
try {
|
|
143
|
+
response = await request(true);
|
|
144
|
+
}
|
|
145
|
+
catch (error) {
|
|
146
|
+
// 構造化出力のスキーマ上限は公式に文書化されておらず、実行してみるまで分からない。
|
|
147
|
+
// 弾かれたら JSON を直接書かせる方式に切り替える。
|
|
148
|
+
// どちらの経路でも結果はコアの検証を通すので、扱いは変わらない。
|
|
149
|
+
if (!isSchemaRejected(error))
|
|
150
|
+
throw error;
|
|
151
|
+
notify({ type: "schemaFallback", reason: messageOf(error) });
|
|
152
|
+
usedSchema = false;
|
|
153
|
+
response = await request(false);
|
|
154
|
+
}
|
|
155
|
+
notify({
|
|
156
|
+
type: "done",
|
|
157
|
+
inputTokens: response.usage.input_tokens,
|
|
158
|
+
outputTokens: response.usage.output_tokens,
|
|
159
|
+
});
|
|
160
|
+
}
|
|
161
|
+
catch (error) {
|
|
162
|
+
if (error instanceof Anthropic.APIConnectionTimeoutError) {
|
|
163
|
+
throw new AppSpecGenerationError(`${Math.round((options.timeoutMs ?? DEFAULT_TIMEOUT_MS) / 1000)} 秒待っても応答がなかったため打ち切りました。\n` +
|
|
164
|
+
" - ネットワークやプロキシの設定を確認してください\n" +
|
|
165
|
+
" - 要件が複雑な場合は、短く区切って試してください\n" +
|
|
166
|
+
" - --verbose を付けると、サーバーから届いたイベントを逐次表示します", error);
|
|
167
|
+
}
|
|
168
|
+
// その他の API / ネットワーク障害はメッセージがそのまま役に立つので触らない。
|
|
169
|
+
if (error instanceof Anthropic.APIError)
|
|
170
|
+
throw error;
|
|
171
|
+
if (isMissingCredentials(error)) {
|
|
172
|
+
throw new AppSpecGenerationError(MISSING_CREDENTIALS_MESSAGE, error);
|
|
173
|
+
}
|
|
174
|
+
throw new AppSpecGenerationError(`アプリ設計の生成に失敗しました: ${messageOf(error)}`, error);
|
|
175
|
+
}
|
|
176
|
+
if (response.stop_reason === "refusal") {
|
|
177
|
+
throw new AppSpecGenerationError("モデルが応答を差し戻しました。プロンプトの内容を見直してください。" +
|
|
178
|
+
(response.stop_details?.explanation === undefined
|
|
179
|
+
? ""
|
|
180
|
+
: `\n${response.stop_details.explanation}`));
|
|
181
|
+
}
|
|
182
|
+
const raw = usedSchema
|
|
183
|
+
? response.parsed_output
|
|
184
|
+
: llmAppSpecLenientSchema.safeParse(parseSpecFromText(response)).data;
|
|
185
|
+
if (raw === null || raw === undefined) {
|
|
186
|
+
throw new AppSpecGenerationError("アプリ設計の生成に失敗しました (応答を JSON として解釈できませんでした)。" +
|
|
187
|
+
" プロンプトをより具体的にして再実行してください。");
|
|
188
|
+
}
|
|
189
|
+
options.onRawSpec?.(raw);
|
|
190
|
+
try {
|
|
191
|
+
return parseAppSpec(withInheritedFromBase(toAppSpecInput(raw), options.base));
|
|
192
|
+
}
|
|
193
|
+
catch (error) {
|
|
194
|
+
throw new AppSpecGenerationError("生成されたアプリ設計が kintone の制約を満たしていませんでした。" +
|
|
195
|
+
" プロンプトを調整して再実行してください。", error);
|
|
196
|
+
}
|
|
197
|
+
}
|
|
198
|
+
/**
|
|
199
|
+
* LLM の平らな出力を AppSpec の入力形に整える。
|
|
200
|
+
*
|
|
201
|
+
* 構造化出力では「指定しない」を表現するために null を使わせているので、
|
|
202
|
+
* ここで null を落として、型ごとに意味のあるキーだけを残す。
|
|
203
|
+
* 余計なキーが残ると AppSpec 側の strict() で弾かれてしまうため、この選別が必要になる。
|
|
204
|
+
*/
|
|
205
|
+
export function toAppSpecInput(raw) {
|
|
206
|
+
const spec = {
|
|
207
|
+
name: raw.name,
|
|
208
|
+
fields: raw.fields.map(toFieldInput),
|
|
209
|
+
};
|
|
210
|
+
assign(spec, "description", raw.description);
|
|
211
|
+
assign(spec, "theme", raw.theme);
|
|
212
|
+
assign(spec, "icon", raw.icon);
|
|
213
|
+
if (raw.views.length > 0) {
|
|
214
|
+
spec["views"] = raw.views.map((view) => ({
|
|
215
|
+
name: view.name,
|
|
216
|
+
type: "LIST",
|
|
217
|
+
...(view.fields.length > 0 ? { fields: view.fields } : {}),
|
|
218
|
+
}));
|
|
219
|
+
}
|
|
220
|
+
if (raw.titleFieldCode !== "") {
|
|
221
|
+
spec["settings"] = { titleFieldCode: raw.titleFieldCode };
|
|
222
|
+
}
|
|
223
|
+
return spec;
|
|
224
|
+
}
|
|
225
|
+
function toFieldInput(field) {
|
|
226
|
+
const result = {
|
|
227
|
+
type: field.type,
|
|
228
|
+
label: field.label,
|
|
229
|
+
required: field.required,
|
|
230
|
+
};
|
|
231
|
+
assign(result, "code", field.code);
|
|
232
|
+
if (isOptionFieldType(field.type)) {
|
|
233
|
+
result["options"] = field.options;
|
|
234
|
+
}
|
|
235
|
+
if (field.type === "CALC") {
|
|
236
|
+
result["expression"] = field.expression;
|
|
237
|
+
}
|
|
238
|
+
assign(result, "group", field.group);
|
|
239
|
+
return result;
|
|
240
|
+
}
|
|
241
|
+
/** 空文字は「指定なし」を表す取り決めなので落とす。 */
|
|
242
|
+
function assign(target, key, value) {
|
|
243
|
+
if (value !== null && value !== undefined && value !== "") {
|
|
244
|
+
target[key] = value;
|
|
245
|
+
}
|
|
246
|
+
}
|
|
247
|
+
export const MISSING_CREDENTIALS_MESSAGE = "Claude API の認証情報が見つかりません。次のいずれかを設定してください:\n" +
|
|
248
|
+
" - .env または環境変数に ANTHROPIC_API_KEY を設定する\n" +
|
|
249
|
+
" - Anthropic CLI (`ant auth login`) でログインする\n" +
|
|
250
|
+
"kintone の認証 (`fieldsmith login`) とは別に必要です。";
|
|
251
|
+
/** Anthropic SDK が資格情報を解決できなかったときのエラーか。 */
|
|
252
|
+
function isMissingCredentials(error) {
|
|
253
|
+
return (error instanceof Error && /Could not resolve authentication method/i.test(error.message));
|
|
254
|
+
}
|
|
255
|
+
function messageOf(error) {
|
|
256
|
+
return error instanceof Error ? error.message : String(error);
|
|
257
|
+
}
|
|
258
|
+
/** 構造化出力のスキーマが受け付けられなかったエラーか。 */
|
|
259
|
+
function isSchemaRejected(error) {
|
|
260
|
+
return (error instanceof Anthropic.BadRequestError &&
|
|
261
|
+
/schema/i.test(typeof error.message === "string" ? error.message : ""));
|
|
262
|
+
}
|
|
263
|
+
/**
|
|
264
|
+
* 構造化出力を使わない場合の応答から JSON を取り出す。
|
|
265
|
+
* 前後に説明やコードブロックが付くことがあるので、最初の `{` から最後の `}` までを拾う。
|
|
266
|
+
*/
|
|
267
|
+
export function parseSpecFromText(response) {
|
|
268
|
+
const text = response.content
|
|
269
|
+
.filter((block) => block.type === "text")
|
|
270
|
+
.map((block) => block.text ?? "")
|
|
271
|
+
.join("");
|
|
272
|
+
const start = text.indexOf("{");
|
|
273
|
+
const end = text.lastIndexOf("}");
|
|
274
|
+
if (start === -1 || end <= start)
|
|
275
|
+
return null;
|
|
276
|
+
try {
|
|
277
|
+
return JSON.parse(text.slice(start, end + 1));
|
|
278
|
+
}
|
|
279
|
+
catch {
|
|
280
|
+
return null;
|
|
281
|
+
}
|
|
282
|
+
}
|
|
283
|
+
/**
|
|
284
|
+
* モデルに渡す本文を組み立てる。
|
|
285
|
+
* 直す場合は、いまの設計をそのまま見せてから依頼を書く。
|
|
286
|
+
*/
|
|
287
|
+
function buildUserMessage(prompt, base) {
|
|
288
|
+
if (base === undefined)
|
|
289
|
+
return prompt;
|
|
290
|
+
return ("## いま動いているアプリの設計\n\n" +
|
|
291
|
+
"```json\n" +
|
|
292
|
+
`${JSON.stringify(base, null, 2)}\n` +
|
|
293
|
+
"```\n\n" +
|
|
294
|
+
"## 変更してほしいこと\n\n" +
|
|
295
|
+
prompt);
|
|
296
|
+
}
|
|
297
|
+
/**
|
|
298
|
+
* 直す場合に、モデルが表現できない項目を元の設計から引き継ぐ。
|
|
299
|
+
*
|
|
300
|
+
* `layout` と `icon` は LLM のスキーマに無い (前者は構造化出力の上限、
|
|
301
|
+
* 後者は kintone から絵文字として取り出せないため)。
|
|
302
|
+
* 引き継がないと、頼んでいないレイアウトの組み直しやアイコンの付け替えが起きる。
|
|
303
|
+
*/
|
|
304
|
+
function withInheritedFromBase(spec, base) {
|
|
305
|
+
if (base === undefined)
|
|
306
|
+
return spec;
|
|
307
|
+
const inherited = { ...spec };
|
|
308
|
+
if (base.layout !== undefined)
|
|
309
|
+
inherited["layout"] = base.layout;
|
|
310
|
+
if (spec["icon"] === undefined && base.icon !== undefined)
|
|
311
|
+
inherited["icon"] = base.icon;
|
|
312
|
+
// 元にアイコンが無いなら、頼まれてもいないのに付けない。
|
|
313
|
+
if (base.icon === undefined)
|
|
314
|
+
delete inherited["icon"];
|
|
315
|
+
return inherited;
|
|
316
|
+
}
|
|
317
|
+
//# sourceMappingURL=generate.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"generate.js","sourceRoot":"","sources":["../../src/llm/generate.ts"],"names":[],"mappings":"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"}
|