@minimalcorp/tsunagi 0.3.3 → 0.4.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/.next/standalone/apps/web/.next/BUILD_ID +1 -1
- package/.next/standalone/apps/web/.next/build-manifest.json +3 -3
- package/.next/standalone/apps/web/.next/prerender-manifest.json +3 -3
- package/.next/standalone/apps/web/.next/server/app/_global-error.html +1 -1
- package/.next/standalone/apps/web/.next/server/app/_global-error.rsc +1 -1
- package/.next/standalone/apps/web/.next/server/app/_global-error.segments/__PAGE__.segment.rsc +1 -1
- package/.next/standalone/apps/web/.next/server/app/_global-error.segments/_full.segment.rsc +1 -1
- package/.next/standalone/apps/web/.next/server/app/_global-error.segments/_head.segment.rsc +1 -1
- package/.next/standalone/apps/web/.next/server/app/_global-error.segments/_index.segment.rsc +1 -1
- package/.next/standalone/apps/web/.next/server/app/_global-error.segments/_tree.segment.rsc +1 -1
- package/.next/standalone/apps/web/.next/server/app/_not-found/page_client-reference-manifest.js +1 -1
- package/.next/standalone/apps/web/.next/server/app/_not-found.html +1 -1
- package/.next/standalone/apps/web/.next/server/app/_not-found.rsc +2 -2
- package/.next/standalone/apps/web/.next/server/app/_not-found.segments/_full.segment.rsc +2 -2
- package/.next/standalone/apps/web/.next/server/app/_not-found.segments/_head.segment.rsc +1 -1
- package/.next/standalone/apps/web/.next/server/app/_not-found.segments/_index.segment.rsc +2 -2
- package/.next/standalone/apps/web/.next/server/app/_not-found.segments/_not-found/__PAGE__.segment.rsc +1 -1
- package/.next/standalone/apps/web/.next/server/app/_not-found.segments/_not-found.segment.rsc +1 -1
- package/.next/standalone/apps/web/.next/server/app/_not-found.segments/_tree.segment.rsc +2 -2
- package/.next/standalone/apps/web/.next/server/app/index.html +1 -1
- package/.next/standalone/apps/web/.next/server/app/index.rsc +3 -3
- package/.next/standalone/apps/web/.next/server/app/index.segments/__PAGE__.segment.rsc +2 -2
- package/.next/standalone/apps/web/.next/server/app/index.segments/_full.segment.rsc +3 -3
- package/.next/standalone/apps/web/.next/server/app/index.segments/_head.segment.rsc +1 -1
- package/.next/standalone/apps/web/.next/server/app/index.segments/_index.segment.rsc +2 -2
- package/.next/standalone/apps/web/.next/server/app/index.segments/_tree.segment.rsc +2 -2
- package/.next/standalone/apps/web/.next/server/app/page.js.nft.json +1 -1
- package/.next/standalone/apps/web/.next/server/app/page_client-reference-manifest.js +1 -1
- package/.next/standalone/apps/web/.next/server/app/settings/page.js.nft.json +1 -1
- package/.next/standalone/apps/web/.next/server/app/settings/page_client-reference-manifest.js +1 -1
- package/.next/standalone/apps/web/.next/server/app/settings.html +1 -1
- package/.next/standalone/apps/web/.next/server/app/settings.rsc +3 -3
- package/.next/standalone/apps/web/.next/server/app/settings.segments/_full.segment.rsc +3 -3
- package/.next/standalone/apps/web/.next/server/app/settings.segments/_head.segment.rsc +1 -1
- package/.next/standalone/apps/web/.next/server/app/settings.segments/_index.segment.rsc +2 -2
- package/.next/standalone/apps/web/.next/server/app/settings.segments/_tree.segment.rsc +2 -2
- package/.next/standalone/apps/web/.next/server/app/settings.segments/settings/__PAGE__.segment.rsc +2 -2
- package/.next/standalone/apps/web/.next/server/app/settings.segments/settings.segment.rsc +1 -1
- package/.next/standalone/apps/web/.next/server/app/tasks/[id]/page.js.nft.json +1 -1
- package/.next/standalone/apps/web/.next/server/app/tasks/[id]/page_client-reference-manifest.js +1 -1
- package/.next/standalone/apps/web/.next/server/chunks/ssr/_05n88to._.js +3 -0
- package/.next/standalone/apps/web/.next/server/chunks/ssr/_0g.cd6a._.js +2 -2
- package/.next/standalone/apps/web/.next/server/chunks/ssr/{_0kflgfj._.js → _0pw3_r_._.js} +3 -3
- package/.next/standalone/apps/web/.next/server/chunks/ssr/{_0g79jqo._.js → _0rb6-mo._.js} +3 -3
- package/.next/standalone/apps/web/.next/server/chunks/ssr/_0ts4rpp._.js +3 -0
- package/.next/standalone/apps/web/.next/server/chunks/ssr/apps_web_src_app_settings_page_tsx_0i6itlb._.js +2 -2
- package/.next/standalone/apps/web/.next/server/chunks/ssr/apps_web_src_app_tasks_[id]_page_tsx_0jshsm2._.js +3 -3
- package/.next/standalone/apps/web/.next/server/chunks/ssr/{node_modules_04pfam-._.js → node_modules_03-rvdo._.js} +1 -1
- package/.next/standalone/apps/web/.next/server/chunks/ssr/{node_modules_0cv326~._.js → node_modules_06jdmu~._.js} +1 -1
- package/.next/standalone/apps/web/.next/server/chunks/ssr/{node_modules_0a57phw._.js → node_modules_0925te2._.js} +2 -2
- package/.next/standalone/apps/web/.next/server/chunks/ssr/{node_modules_0_huoqd._.js → node_modules_0e8uni7._.js} +1 -1
- package/.next/standalone/apps/web/.next/server/chunks/ssr/{node_modules_0tod6y8._.js → node_modules_0h4h_aw._.js} +1 -1
- package/.next/standalone/apps/web/.next/server/chunks/ssr/{node_modules_0h.vgyn._.js → node_modules_0nzbb9~._.js} +1 -1
- package/.next/standalone/apps/web/.next/server/chunks/ssr/{node_modules_0oip5a1._.js → node_modules_0q8yuvq._.js} +1 -1
- package/.next/standalone/apps/web/.next/server/chunks/ssr/{node_modules_11-z65a._.js → node_modules_0r6t2tu._.js} +1 -1
- package/.next/standalone/apps/web/.next/server/chunks/ssr/{node_modules_09d2oqj._.js → node_modules_0t6ysh_._.js} +1 -1
- package/.next/standalone/apps/web/.next/server/chunks/ssr/{node_modules_0krwgny._.js → node_modules_0vj~e_v._.js} +1 -1
- package/.next/standalone/apps/web/.next/server/chunks/ssr/{node_modules_0sn0lqb._.js → node_modules_0vq~hzy._.js} +1 -1
- package/.next/standalone/apps/web/.next/server/chunks/ssr/{node_modules_0l23gkf._.js → node_modules_0xzmx-x._.js} +1 -1
- package/.next/standalone/apps/web/.next/server/chunks/ssr/{node_modules_0npujym._.js → node_modules_0yb2sh9._.js} +1 -1
- package/.next/standalone/apps/web/.next/server/chunks/ssr/{node_modules_0q_5qo~._.js → node_modules_0yc~s8p._.js} +1 -1
- package/.next/standalone/apps/web/.next/server/chunks/ssr/{node_modules_0q_xfyl._.js → node_modules_0zi-gfw._.js} +1 -1
- package/.next/standalone/apps/web/.next/server/chunks/ssr/{node_modules_0ak_3rk._.js → node_modules_0zzdoud._.js} +1 -1
- package/.next/standalone/apps/web/.next/server/chunks/ssr/{node_modules_0xp9nhx._.js → node_modules_0~aiu12._.js} +1 -1
- package/.next/standalone/apps/web/.next/server/middleware-build-manifest.js +3 -3
- package/.next/standalone/apps/web/.next/server/pages/404.html +1 -1
- package/.next/standalone/apps/web/.next/server/pages/500.html +1 -1
- package/.next/standalone/apps/web/.next/server/server-reference-manifest.js +1 -1
- package/.next/standalone/apps/web/.next/server/server-reference-manifest.json +1 -1
- package/.next/standalone/apps/web/.next/static/chunks/09b_3duxnyfto.js +7 -0
- package/.next/standalone/apps/web/.next/static/chunks/{0z60ewb40i2ce.js → 0gxhy80~c0gvp.js} +1 -1
- package/.next/standalone/apps/web/.next/static/chunks/0i9h-39w91ybd.js +6 -0
- package/.next/standalone/apps/web/.next/static/chunks/{0b90ys7wq-p.4.css → 0pm0knaijueoc.css} +1 -1
- package/.next/standalone/apps/web/.next/static/chunks/{0oricprxoxb_d.js → 0smdykrt42e.5.js} +10 -10
- package/.next/standalone/apps/web/.next/static/chunks/{149sia28cskw9.js → 0yp~t_.pozr-d.js} +6 -6
- package/.next/standalone/apps/web/.next/static/chunks/{0o6ggm7jrim25.js → 14zh2vf8ljbxo.js} +1 -1
- package/.next/standalone/apps/web/.next/static/chunks/{053h220fng_68.js → 1624992q-gpj2.js} +10 -10
- package/.next/standalone/apps/web/package.json +1 -0
- package/README.md +1 -1
- package/dist/app.js +3 -3
- package/dist/plugin-lifecycle.d.ts +5 -3
- package/dist/plugin-lifecycle.js +40 -16
- package/dist/prisma/migrations/20261011000000_add_codex_support/migration.sql +20 -0
- package/dist/prisma/schema.prisma +9 -5
- package/dist/server/generated/prisma/browser.d.ts +2 -2
- package/dist/server/generated/prisma/client.d.ts +2 -2
- package/dist/server/generated/prisma/internal/class.d.ts +4 -4
- package/dist/server/generated/prisma/internal/class.js +4 -4
- package/dist/server/generated/prisma/internal/prismaNamespace.d.ts +41 -39
- package/dist/server/generated/prisma/internal/prismaNamespace.js +4 -2
- package/dist/server/generated/prisma/internal/prismaNamespaceBrowser.d.ts +5 -3
- package/dist/server/generated/prisma/internal/prismaNamespaceBrowser.js +4 -2
- package/dist/server/generated/prisma/models/AgentProfile.d.ts +972 -0
- package/dist/server/generated/prisma/models/Tab.d.ts +36 -1
- package/dist/server/generated/prisma/models.d.ts +1 -1
- package/dist/server/index.js +2 -2
- package/dist/server/lib/agent-profiles.d.ts +43 -0
- package/dist/server/lib/agent-profiles.js +127 -0
- package/dist/server/lib/agents/claude.d.ts +2 -0
- package/dist/server/lib/agents/claude.js +130 -0
- package/dist/server/lib/agents/cli.d.ts +14 -0
- package/dist/server/lib/agents/cli.js +57 -0
- package/dist/server/lib/agents/codex.d.ts +2 -0
- package/dist/server/lib/agents/codex.js +285 -0
- package/dist/server/lib/agents/index.d.ts +11 -0
- package/dist/server/lib/agents/index.js +19 -0
- package/dist/server/lib/agents/login-callback.d.ts +11 -0
- package/dist/server/lib/agents/login-callback.js +47 -0
- package/dist/server/lib/agents/types.d.ts +77 -0
- package/dist/server/lib/agents/types.js +8 -0
- package/dist/server/lib/pty-env-sync.js +1 -1
- package/dist/server/lib/pty-launch.d.ts +8 -10
- package/dist/server/lib/pty-launch.js +20 -58
- package/dist/server/lib/repositories/task.js +1 -0
- package/dist/server/lib/tab-todos.d.ts +5 -0
- package/dist/server/lib/tab-todos.js +13 -0
- package/dist/server/pty-manager.d.ts +7 -2
- package/dist/server/pty-manager.js +13 -0
- package/dist/server/routes/agent-profiles.d.ts +2 -0
- package/dist/server/routes/agent-profiles.js +193 -0
- package/dist/server/routes/env.js +2 -2
- package/dist/server/routes/hooks.d.ts +4 -19
- package/dist/server/routes/hooks.js +83 -73
- package/dist/server/routes/mcp.js +2 -2
- package/dist/server/routes/onboarding.js +19 -10
- package/dist/server/routes/tasks.js +4 -4
- package/dist/server/routes/terminal.js +47 -34
- package/dist/with-plugin.js +3 -3
- package/npm-shrinkwrap.json +12 -12
- package/package.json +1 -1
- package/tsunagi-marketplace/codex/tsunagi-task.md +120 -0
- package/.next/standalone/apps/web/.next/server/chunks/ssr/_0hnxatt._.js +0 -3
- package/.next/standalone/apps/web/.next/server/chunks/ssr/_10gk93.._.js +0 -3
- package/.next/standalone/apps/web/.next/static/chunks/0crn03y2tj0lu.js +0 -6
- package/.next/standalone/apps/web/.next/static/chunks/0lvl3ec93cab_.js +0 -7
- package/dist/server/generated/prisma/models/ClaudeProfile.d.ts +0 -939
- package/dist/server/lib/claude-profiles.d.ts +0 -49
- package/dist/server/lib/claude-profiles.js +0 -168
- package/dist/server/routes/claude-profiles.d.ts +0 -2
- package/dist/server/routes/claude-profiles.js +0 -138
- /package/.next/standalone/apps/web/.next/static/{7x81GlNKt9yqIkeI7U1BX → ZcOuTrikuO_ZLaERk7FsW}/_buildManifest.js +0 -0
- /package/.next/standalone/apps/web/.next/static/{7x81GlNKt9yqIkeI7U1BX → ZcOuTrikuO_ZLaERk7FsW}/_clientMiddlewareManifest.js +0 -0
- /package/.next/standalone/apps/web/.next/static/{7x81GlNKt9yqIkeI7U1BX → ZcOuTrikuO_ZLaERk7FsW}/_ssgManifest.js +0 -0
- /package/dist/server/generated/prisma/models/{ClaudeProfile.js → AgentProfile.js} +0 -0
|
@@ -1,49 +0,0 @@
|
|
|
1
|
-
import type { ClaudeAuthStatus, ClaudeProfile } from '@minimalcorp/tsunagi-shared';
|
|
2
|
-
/**
|
|
3
|
-
* タブで使う Claude プロファイルの割り当てを保存する予約キー。値はプロファイルの slug か
|
|
4
|
-
* DEFAULT_CLAUDE_PROFILE。環境変数と同じ global → owner → repo の継承で解決し、PTY 起動時に
|
|
5
|
-
* CLAUDE_CONFIG_DIR へ変換する(resolveClaudeProfileEnv)。
|
|
6
|
-
*/
|
|
7
|
-
export declare const CLAUDE_PROFILE_ENV_KEY = "TSUNAGI_CLAUDE_PROFILE";
|
|
8
|
-
/** システム既定(CLAUDE_CONFIG_DIR を付けず ~/.claude を使う) */
|
|
9
|
-
export declare const DEFAULT_CLAUDE_PROFILE = "default";
|
|
10
|
-
/**
|
|
11
|
-
* Claude Code が /login のログインより優先する認証情報の環境変数。
|
|
12
|
-
* プロファイルを選んだタブでは、外側 Terminal から継承した値も取り除く(残るとプロファイルの
|
|
13
|
-
* ログインではなくこちらが使われるため)。
|
|
14
|
-
*/
|
|
15
|
-
export declare const CLAUDE_AUTH_ENV_KEYS: string[];
|
|
16
|
-
/** 汎用の環境変数として登録させないキー(認証は Claude プロファイルで扱う) */
|
|
17
|
-
export declare const RESERVED_ENV_KEYS: string[];
|
|
18
|
-
export declare function getClaudeProfilesDir(): string;
|
|
19
|
-
/** プロファイルの CLAUDE_CONFIG_DIR。システム既定は undefined(~/.claude) */
|
|
20
|
-
export declare function getClaudeProfileConfigDir(slug: string): string | undefined;
|
|
21
|
-
/** 空白を `-` にし、小文字にする(macOS のファイルシステムは大文字小文字を区別しないため) */
|
|
22
|
-
export declare function toClaudeProfileSlug(name: string): string;
|
|
23
|
-
/** プロファイル名の検証。問題があればエラーメッセージを返す */
|
|
24
|
-
export declare function validateClaudeProfileName(name: string): string | null;
|
|
25
|
-
export declare function listClaudeProfiles(): Promise<ClaudeProfile[]>;
|
|
26
|
-
export declare function claudeProfileExists(slug: string): Promise<boolean>;
|
|
27
|
-
export declare function createClaudeProfile(name: string): Promise<ClaudeProfile>;
|
|
28
|
-
/**
|
|
29
|
-
* プロファイルを削除する。ログイン情報(macOS は Keychain)を残さないよう先に logout し、
|
|
30
|
-
* このプロファイルを割り当てていたスコープは割り当てを外して親の設定を継承させる。
|
|
31
|
-
*/
|
|
32
|
-
export declare function deleteClaudeProfile(slug: string): Promise<void>;
|
|
33
|
-
/** `claude auth status --json` でプロファイルのログイン状態を取得する */
|
|
34
|
-
export declare function getClaudeAuthStatus(slug: string): Promise<ClaudeAuthStatus>;
|
|
35
|
-
/** ログイン用 PTY に書き込むコマンド */
|
|
36
|
-
export declare function buildClaudeLoginCommand(): string;
|
|
37
|
-
/** ログイン用 PTY の環境変数(PtyManager.createSession の env / unsetKeys) */
|
|
38
|
-
export declare function claudeLoginPtyEnv(slug: string): {
|
|
39
|
-
env: Record<string, string>;
|
|
40
|
-
unsetKeys: string[];
|
|
41
|
-
};
|
|
42
|
-
/**
|
|
43
|
-
* 継承解決済みの環境変数に含まれるプロファイル割り当てを CLAUDE_CONFIG_DIR に変換する。
|
|
44
|
-
* 予約キー自体は PTY に渡さない。
|
|
45
|
-
*/
|
|
46
|
-
export declare function resolveClaudeProfileEnv(env: Record<string, string>): {
|
|
47
|
-
env: Record<string, string>;
|
|
48
|
-
unsetKeys: string[];
|
|
49
|
-
};
|
|
@@ -1,168 +0,0 @@
|
|
|
1
|
-
import { execFile } from 'child_process';
|
|
2
|
-
import * as fs from 'fs/promises';
|
|
3
|
-
import * as path from 'path';
|
|
4
|
-
import { promisify } from 'util';
|
|
5
|
-
import { prisma } from './db.js';
|
|
6
|
-
import { getTsunagiDataDir } from './data-path.js';
|
|
7
|
-
const execFileAsync = promisify(execFile);
|
|
8
|
-
/**
|
|
9
|
-
* タブで使う Claude プロファイルの割り当てを保存する予約キー。値はプロファイルの slug か
|
|
10
|
-
* DEFAULT_CLAUDE_PROFILE。環境変数と同じ global → owner → repo の継承で解決し、PTY 起動時に
|
|
11
|
-
* CLAUDE_CONFIG_DIR へ変換する(resolveClaudeProfileEnv)。
|
|
12
|
-
*/
|
|
13
|
-
export const CLAUDE_PROFILE_ENV_KEY = 'TSUNAGI_CLAUDE_PROFILE';
|
|
14
|
-
/** システム既定(CLAUDE_CONFIG_DIR を付けず ~/.claude を使う) */
|
|
15
|
-
export const DEFAULT_CLAUDE_PROFILE = 'default';
|
|
16
|
-
/**
|
|
17
|
-
* Claude Code が /login のログインより優先する認証情報の環境変数。
|
|
18
|
-
* プロファイルを選んだタブでは、外側 Terminal から継承した値も取り除く(残るとプロファイルの
|
|
19
|
-
* ログインではなくこちらが使われるため)。
|
|
20
|
-
*/
|
|
21
|
-
export const CLAUDE_AUTH_ENV_KEYS = ['ANTHROPIC_API_KEY', 'CLAUDE_CODE_OAUTH_TOKEN'];
|
|
22
|
-
/** 汎用の環境変数として登録させないキー(認証は Claude プロファイルで扱う) */
|
|
23
|
-
export const RESERVED_ENV_KEYS = [
|
|
24
|
-
CLAUDE_PROFILE_ENV_KEY,
|
|
25
|
-
'CLAUDE_CONFIG_DIR',
|
|
26
|
-
...CLAUDE_AUTH_ENV_KEYS,
|
|
27
|
-
];
|
|
28
|
-
const PROFILE_NAME_PATTERN = /^[A-Za-z0-9 _-]+$/;
|
|
29
|
-
export function getClaudeProfilesDir() {
|
|
30
|
-
return path.join(getTsunagiDataDir(), 'claude-profiles');
|
|
31
|
-
}
|
|
32
|
-
/** プロファイルの CLAUDE_CONFIG_DIR。システム既定は undefined(~/.claude) */
|
|
33
|
-
export function getClaudeProfileConfigDir(slug) {
|
|
34
|
-
if (slug === DEFAULT_CLAUDE_PROFILE)
|
|
35
|
-
return undefined;
|
|
36
|
-
return path.join(getClaudeProfilesDir(), slug);
|
|
37
|
-
}
|
|
38
|
-
/** 空白を `-` にし、小文字にする(macOS のファイルシステムは大文字小文字を区別しないため) */
|
|
39
|
-
export function toClaudeProfileSlug(name) {
|
|
40
|
-
return name.trim().replace(/\s+/g, '-').toLowerCase();
|
|
41
|
-
}
|
|
42
|
-
/** プロファイル名の検証。問題があればエラーメッセージを返す */
|
|
43
|
-
export function validateClaudeProfileName(name) {
|
|
44
|
-
const trimmed = name.trim();
|
|
45
|
-
if (!trimmed)
|
|
46
|
-
return 'Name is required';
|
|
47
|
-
if (!PROFILE_NAME_PATTERN.test(trimmed)) {
|
|
48
|
-
return 'Use only letters, numbers, spaces, "-" and "_"';
|
|
49
|
-
}
|
|
50
|
-
if (toClaudeProfileSlug(trimmed) === DEFAULT_CLAUDE_PROFILE) {
|
|
51
|
-
return `"${DEFAULT_CLAUDE_PROFILE}" is reserved`;
|
|
52
|
-
}
|
|
53
|
-
return null;
|
|
54
|
-
}
|
|
55
|
-
export async function listClaudeProfiles() {
|
|
56
|
-
const rows = await prisma.claudeProfile.findMany({ orderBy: { createdAt: 'asc' } });
|
|
57
|
-
return rows.map((row) => ({
|
|
58
|
-
slug: row.slug,
|
|
59
|
-
name: row.name,
|
|
60
|
-
configDir: path.join(getClaudeProfilesDir(), row.slug),
|
|
61
|
-
}));
|
|
62
|
-
}
|
|
63
|
-
export async function claudeProfileExists(slug) {
|
|
64
|
-
if (slug === DEFAULT_CLAUDE_PROFILE)
|
|
65
|
-
return true;
|
|
66
|
-
return (await prisma.claudeProfile.count({ where: { slug } })) > 0;
|
|
67
|
-
}
|
|
68
|
-
export async function createClaudeProfile(name) {
|
|
69
|
-
const error = validateClaudeProfileName(name);
|
|
70
|
-
if (error)
|
|
71
|
-
throw new Error(error);
|
|
72
|
-
const trimmed = name.trim();
|
|
73
|
-
const slug = toClaudeProfileSlug(trimmed);
|
|
74
|
-
if (await claudeProfileExists(slug)) {
|
|
75
|
-
throw new Error(`Profile "${slug}" already exists`);
|
|
76
|
-
}
|
|
77
|
-
const configDir = path.join(getClaudeProfilesDir(), slug);
|
|
78
|
-
await fs.mkdir(configDir, { recursive: true });
|
|
79
|
-
await prisma.claudeProfile.create({ data: { slug, name: trimmed } });
|
|
80
|
-
return { slug, name: trimmed, configDir };
|
|
81
|
-
}
|
|
82
|
-
/**
|
|
83
|
-
* プロファイルを削除する。ログイン情報(macOS は Keychain)を残さないよう先に logout し、
|
|
84
|
-
* このプロファイルを割り当てていたスコープは割り当てを外して親の設定を継承させる。
|
|
85
|
-
*/
|
|
86
|
-
export async function deleteClaudeProfile(slug) {
|
|
87
|
-
const configDir = getClaudeProfileConfigDir(slug);
|
|
88
|
-
if (!configDir)
|
|
89
|
-
throw new Error('The system default profile cannot be deleted');
|
|
90
|
-
await runClaude(['auth', 'logout'], configDir).catch(() => undefined);
|
|
91
|
-
await prisma.environmentVariable.deleteMany({
|
|
92
|
-
where: { key: CLAUDE_PROFILE_ENV_KEY, value: slug },
|
|
93
|
-
});
|
|
94
|
-
await prisma.claudeProfile.delete({ where: { slug } });
|
|
95
|
-
await fs.rm(configDir, { recursive: true, force: true });
|
|
96
|
-
}
|
|
97
|
-
/**
|
|
98
|
-
* claude を実行するときの環境変数。プロファイルのログインを見るよう CLAUDE_CONFIG_DIR を付け、
|
|
99
|
-
* ログインより優先される認証情報の環境変数は取り除く。システム既定は外側 Terminal の環境のまま
|
|
100
|
-
* (普段ターミナルで claude を使うときと同じ)。
|
|
101
|
-
*/
|
|
102
|
-
function claudeEnv(configDir) {
|
|
103
|
-
const env = { ...process.env };
|
|
104
|
-
if (configDir) {
|
|
105
|
-
env.CLAUDE_CONFIG_DIR = configDir;
|
|
106
|
-
for (const key of CLAUDE_AUTH_ENV_KEYS)
|
|
107
|
-
delete env[key];
|
|
108
|
-
}
|
|
109
|
-
return env;
|
|
110
|
-
}
|
|
111
|
-
async function runClaude(args, configDir) {
|
|
112
|
-
const { stdout } = await execFileAsync('claude', args, {
|
|
113
|
-
env: claudeEnv(configDir),
|
|
114
|
-
timeout: 15_000,
|
|
115
|
-
});
|
|
116
|
-
return stdout;
|
|
117
|
-
}
|
|
118
|
-
/** `claude auth status --json` でプロファイルのログイン状態を取得する */
|
|
119
|
-
export async function getClaudeAuthStatus(slug) {
|
|
120
|
-
try {
|
|
121
|
-
const raw = JSON.parse(await runClaude(['auth', 'status', '--json'], getClaudeProfileConfigDir(slug)));
|
|
122
|
-
const str = (v) => (typeof v === 'string' ? v : undefined);
|
|
123
|
-
return {
|
|
124
|
-
loggedIn: raw.loggedIn === true,
|
|
125
|
-
authMethod: str(raw.authMethod),
|
|
126
|
-
email: str(raw.email),
|
|
127
|
-
orgName: str(raw.orgName),
|
|
128
|
-
subscriptionType: str(raw.subscriptionType),
|
|
129
|
-
};
|
|
130
|
-
}
|
|
131
|
-
catch (err) {
|
|
132
|
-
// claude auth status は未ログイン時に非 0 で終了するが、stdout には JSON が出る
|
|
133
|
-
const stdout = err.stdout;
|
|
134
|
-
if (stdout) {
|
|
135
|
-
try {
|
|
136
|
-
const raw = JSON.parse(stdout);
|
|
137
|
-
return { loggedIn: raw.loggedIn === true, authMethod: raw.authMethod };
|
|
138
|
-
}
|
|
139
|
-
catch {
|
|
140
|
-
// fallthrough
|
|
141
|
-
}
|
|
142
|
-
}
|
|
143
|
-
return { loggedIn: false, error: err instanceof Error ? err.message : String(err) };
|
|
144
|
-
}
|
|
145
|
-
}
|
|
146
|
-
/** ログイン用 PTY に書き込むコマンド */
|
|
147
|
-
export function buildClaudeLoginCommand() {
|
|
148
|
-
return 'claude auth login';
|
|
149
|
-
}
|
|
150
|
-
/** ログイン用 PTY の環境変数(PtyManager.createSession の env / unsetKeys) */
|
|
151
|
-
export function claudeLoginPtyEnv(slug) {
|
|
152
|
-
const configDir = getClaudeProfileConfigDir(slug);
|
|
153
|
-
if (!configDir)
|
|
154
|
-
return { env: {}, unsetKeys: [] };
|
|
155
|
-
return { env: { CLAUDE_CONFIG_DIR: configDir }, unsetKeys: CLAUDE_AUTH_ENV_KEYS };
|
|
156
|
-
}
|
|
157
|
-
/**
|
|
158
|
-
* 継承解決済みの環境変数に含まれるプロファイル割り当てを CLAUDE_CONFIG_DIR に変換する。
|
|
159
|
-
* 予約キー自体は PTY に渡さない。
|
|
160
|
-
*/
|
|
161
|
-
export function resolveClaudeProfileEnv(env) {
|
|
162
|
-
const { [CLAUDE_PROFILE_ENV_KEY]: slug, ...rest } = env;
|
|
163
|
-
const configDir = slug ? getClaudeProfileConfigDir(slug) : undefined;
|
|
164
|
-
// システム既定: 外側 Terminal の環境のまま(普段ターミナルで claude を使うときと同じ)
|
|
165
|
-
if (!configDir)
|
|
166
|
-
return { env: rest, unsetKeys: [] };
|
|
167
|
-
return { env: { ...rest, CLAUDE_CONFIG_DIR: configDir }, unsetKeys: CLAUDE_AUTH_ENV_KEYS };
|
|
168
|
-
}
|
|
@@ -1,138 +0,0 @@
|
|
|
1
|
-
import * as os from 'os';
|
|
2
|
-
import { ptyManager } from '../pty-manager.js';
|
|
3
|
-
import { prisma } from '../lib/db.js';
|
|
4
|
-
import { deleteEnv, getEnv, setEnv } from '../lib/repositories/environment.js';
|
|
5
|
-
import { CLAUDE_PROFILE_ENV_KEY, DEFAULT_CLAUDE_PROFILE, buildClaudeLoginCommand, claudeLoginPtyEnv, claudeProfileExists, createClaudeProfile, deleteClaudeProfile, getClaudeAuthStatus, listClaudeProfiles, } from '../lib/claude-profiles.js';
|
|
6
|
-
import { notifyEnvChanged } from '../lib/pty-env-sync.js';
|
|
7
|
-
/** ログイン用 PTY の sessionId の接頭辞(タブの PTY と区別する) */
|
|
8
|
-
const LOGIN_SESSION_PREFIX = 'claude-login-';
|
|
9
|
-
function validateScope(scope, owner, repo) {
|
|
10
|
-
if (scope !== 'global' && scope !== 'owner' && scope !== 'repo')
|
|
11
|
-
return 'Invalid scope';
|
|
12
|
-
if (scope === 'owner' && !owner)
|
|
13
|
-
return 'owner is required for scope=owner';
|
|
14
|
-
if (scope === 'repo' && (!owner || !repo))
|
|
15
|
-
return 'owner and repo are required for scope=repo';
|
|
16
|
-
return null;
|
|
17
|
-
}
|
|
18
|
-
async function getAssignment(scope, owner, repo) {
|
|
19
|
-
const row = await prisma.environmentVariable.findFirst({
|
|
20
|
-
where: {
|
|
21
|
-
key: CLAUDE_PROFILE_ENV_KEY,
|
|
22
|
-
scope,
|
|
23
|
-
...(scope !== 'global' && { owner }),
|
|
24
|
-
...(scope === 'repo' && { repo }),
|
|
25
|
-
},
|
|
26
|
-
});
|
|
27
|
-
const merged = await getEnv(scope, owner, repo);
|
|
28
|
-
const assignment = {
|
|
29
|
-
assigned: row?.value ?? null,
|
|
30
|
-
effective: merged[CLAUDE_PROFILE_ENV_KEY] ?? DEFAULT_CLAUDE_PROFILE,
|
|
31
|
-
};
|
|
32
|
-
return assignment;
|
|
33
|
-
}
|
|
34
|
-
function syncPtyEnv(fastify) {
|
|
35
|
-
notifyEnvChanged().catch((err) => fastify.log.warn(err, 'Failed to sync PTY env'));
|
|
36
|
-
}
|
|
37
|
-
export async function claudeProfilesRoutes(fastify) {
|
|
38
|
-
// GET /claude-profiles - システム既定 + 登録済みプロファイルとログイン状態
|
|
39
|
-
fastify.get('/claude-profiles', async (_request, reply) => {
|
|
40
|
-
const profiles = await listClaudeProfiles();
|
|
41
|
-
const data = await Promise.all([
|
|
42
|
-
getClaudeAuthStatus(DEFAULT_CLAUDE_PROFILE).then((status) => ({
|
|
43
|
-
slug: DEFAULT_CLAUDE_PROFILE,
|
|
44
|
-
name: 'System default',
|
|
45
|
-
// CLAUDE_CONFIG_DIR を付けない(普段ターミナルで claude を使うときと同じ設定)
|
|
46
|
-
configDir: null,
|
|
47
|
-
status,
|
|
48
|
-
})),
|
|
49
|
-
...profiles.map(async (profile) => ({
|
|
50
|
-
...profile,
|
|
51
|
-
status: await getClaudeAuthStatus(profile.slug),
|
|
52
|
-
})),
|
|
53
|
-
]);
|
|
54
|
-
return reply.send({ data: { profiles: data } });
|
|
55
|
-
});
|
|
56
|
-
// GET /claude-profiles/:slug/status - ログイン状態(ログイン用ダイアログで完了を確認する)
|
|
57
|
-
fastify.get('/claude-profiles/:slug/status', async (request, reply) => {
|
|
58
|
-
const { slug } = request.params;
|
|
59
|
-
if (!(await claudeProfileExists(slug))) {
|
|
60
|
-
return reply.status(404).send({ error: 'Profile not found' });
|
|
61
|
-
}
|
|
62
|
-
return reply.send({ data: { status: await getClaudeAuthStatus(slug) } });
|
|
63
|
-
});
|
|
64
|
-
// POST /claude-profiles - プロファイル作成
|
|
65
|
-
fastify.post('/claude-profiles', async (request, reply) => {
|
|
66
|
-
try {
|
|
67
|
-
const profile = await createClaudeProfile(request.body?.name ?? '');
|
|
68
|
-
return reply.status(201).send({ data: { profile } });
|
|
69
|
-
}
|
|
70
|
-
catch (err) {
|
|
71
|
-
return reply.status(400).send({ error: err instanceof Error ? err.message : String(err) });
|
|
72
|
-
}
|
|
73
|
-
});
|
|
74
|
-
// DELETE /claude-profiles/:slug - プロファイル削除(logout してディレクトリごと消す)
|
|
75
|
-
fastify.delete('/claude-profiles/:slug', async (request, reply) => {
|
|
76
|
-
const { slug } = request.params;
|
|
77
|
-
if (slug === DEFAULT_CLAUDE_PROFILE || !(await claudeProfileExists(slug))) {
|
|
78
|
-
return reply.status(404).send({ error: 'Profile not found' });
|
|
79
|
-
}
|
|
80
|
-
await deleteClaudeProfile(slug);
|
|
81
|
-
syncPtyEnv(fastify);
|
|
82
|
-
return reply.status(204).send();
|
|
83
|
-
});
|
|
84
|
-
// POST /claude-profiles/:slug/login - ログイン用 PTY を起動し `claude auth login` を実行する
|
|
85
|
-
fastify.post('/claude-profiles/:slug/login', async (request, reply) => {
|
|
86
|
-
const { slug } = request.params;
|
|
87
|
-
if (!(await claudeProfileExists(slug))) {
|
|
88
|
-
return reply.status(404).send({ error: 'Profile not found' });
|
|
89
|
-
}
|
|
90
|
-
// 同じプロファイルのログイン用 PTY が残っていれば閉じる
|
|
91
|
-
const prefix = `${LOGIN_SESSION_PREFIX}${slug}-`;
|
|
92
|
-
for (const id of ptyManager.listSessions()) {
|
|
93
|
-
if (id.startsWith(prefix))
|
|
94
|
-
ptyManager.deleteSession(id);
|
|
95
|
-
}
|
|
96
|
-
const sessionId = `${prefix}${crypto.randomUUID()}`;
|
|
97
|
-
const { env, unsetKeys } = claudeLoginPtyEnv(slug);
|
|
98
|
-
const session = ptyManager.createSession(sessionId, os.homedir(), env, unsetKeys);
|
|
99
|
-
// シェルの初期化(プロンプト表示)を待つため少し遅延させて書き込む
|
|
100
|
-
setTimeout(() => session.pty.write(`${buildClaudeLoginCommand()}\n`), 300);
|
|
101
|
-
return reply.status(201).send({ data: { sessionId } });
|
|
102
|
-
});
|
|
103
|
-
// DELETE /claude-profiles/login/:sessionId - ログイン用 PTY を閉じる
|
|
104
|
-
fastify.delete('/claude-profiles/login/:sessionId', async (request, reply) => {
|
|
105
|
-
const { sessionId } = request.params;
|
|
106
|
-
if (!sessionId.startsWith(LOGIN_SESSION_PREFIX)) {
|
|
107
|
-
return reply.status(400).send({ error: 'Not a login session' });
|
|
108
|
-
}
|
|
109
|
-
ptyManager.deleteSession(sessionId);
|
|
110
|
-
return reply.status(204).send();
|
|
111
|
-
});
|
|
112
|
-
// GET /claude-profiles/assignment - スコープのプロファイル割り当て
|
|
113
|
-
fastify.get('/claude-profiles/assignment', async (request, reply) => {
|
|
114
|
-
const { scope, owner, repo } = request.query;
|
|
115
|
-
const error = validateScope(scope, owner, repo);
|
|
116
|
-
if (error)
|
|
117
|
-
return reply.status(400).send({ error });
|
|
118
|
-
return reply.send({ data: { assignment: await getAssignment(scope, owner, repo) } });
|
|
119
|
-
});
|
|
120
|
-
// PUT /claude-profiles/assignment - スコープのプロファイル割り当てを変更する
|
|
121
|
-
fastify.put('/claude-profiles/assignment', async (request, reply) => {
|
|
122
|
-
const { scope, owner, repo, slug } = request.body ?? {};
|
|
123
|
-
const error = validateScope(scope, owner, repo);
|
|
124
|
-
if (error)
|
|
125
|
-
return reply.status(400).send({ error });
|
|
126
|
-
if (slug !== null && !(await claudeProfileExists(slug))) {
|
|
127
|
-
return reply.status(400).send({ error: 'Profile not found' });
|
|
128
|
-
}
|
|
129
|
-
if (slug === null) {
|
|
130
|
-
await deleteEnv(CLAUDE_PROFILE_ENV_KEY, scope, owner, repo);
|
|
131
|
-
}
|
|
132
|
-
else {
|
|
133
|
-
await setEnv(CLAUDE_PROFILE_ENV_KEY, slug, scope, owner, repo);
|
|
134
|
-
}
|
|
135
|
-
syncPtyEnv(fastify);
|
|
136
|
-
return reply.send({ data: { assignment: await getAssignment(scope, owner, repo) } });
|
|
137
|
-
});
|
|
138
|
-
}
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|