ai_co_discuss 0.2.84 → 0.2.86
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/backend/server.py +3 -3
- package/package.json +1 -1
- package/runner/discussion_launch.mjs +46 -5
- package/runner/free_runner.mjs +463 -452
- package/web/app.js +44 -18
- package/web/modern.css +1 -1
- package/web/modern.js +1323 -1157
package/backend/server.py
CHANGED
|
@@ -1851,7 +1851,7 @@ class DiscussionHandler(BaseHTTPRequestHandler):
|
|
|
1851
1851
|
"""Forward authenticated browser requests to the shared Node entrypoint and pin their website."""
|
|
1852
1852
|
site = self.local_request_site(method)
|
|
1853
1853
|
route = parts[2:]
|
|
1854
|
-
if method == "OPTIONS" and route in (["settings"], ["launch"], ["room-models"], ["room-member"], ["room-member-access"], ["room-profiles"], ["room-restore"], ["models"], ["api-key"], ["api-check"],
|
|
1854
|
+
if method == "OPTIONS" and route in (["settings"], ["launch"], ["room-models"], ["room-member"], ["room-member-access"], ["room-member-adapter"], ["room-profiles"], ["room-restore"], ["models"], ["api-key"], ["api-check"],
|
|
1855
1855
|
["scheduler-check"], ["pick-directory"], ["projects"], ["machine-link"]):
|
|
1856
1856
|
return self.respond({"ok": True})
|
|
1857
1857
|
if route == ["settings"] and method in ("GET", "POST"):
|
|
@@ -1870,7 +1870,7 @@ class DiscussionHandler(BaseHTTPRequestHandler):
|
|
|
1870
1870
|
command = "api-check"
|
|
1871
1871
|
elif route == ["scheduler-check"] and method == "POST":
|
|
1872
1872
|
command = "scheduler-check"
|
|
1873
|
-
elif route in (["room-member"], ["room-member-access"]) and method == "POST":
|
|
1873
|
+
elif route in (["room-member"], ["room-member-access"], ["room-member-adapter"]) and method == "POST":
|
|
1874
1874
|
command = route[0]
|
|
1875
1875
|
elif route in (["room-profiles"], ["room-restore"]) and method == "POST":
|
|
1876
1876
|
command = route[0]
|
|
@@ -1887,7 +1887,7 @@ class DiscussionHandler(BaseHTTPRequestHandler):
|
|
|
1887
1887
|
if self.headers.get_content_type() != "application/json":
|
|
1888
1888
|
raise ValueError("本机操作须使用 application/json")
|
|
1889
1889
|
payload = self.read_payload()
|
|
1890
|
-
if command in ("start", "room-config", "room-models", "room-member", "room-member-access", "room-profiles", "room-restore"):
|
|
1890
|
+
if command in ("start", "room-config", "room-models", "room-member", "room-member-access", "room-member-adapter", "room-profiles", "room-restore"):
|
|
1891
1891
|
payload.pop("access_file", None)
|
|
1892
1892
|
payload.update(site=site, entry="web")
|
|
1893
1893
|
if command == "machine-link":
|
package/package.json
CHANGED
|
@@ -53,6 +53,7 @@ const HELP = `用法:codiscuss launch settings [--file FILE]
|
|
|
53
53
|
codiscuss launch scheduler-check --file FILE
|
|
54
54
|
codiscuss launch room-member --file FILE
|
|
55
55
|
codiscuss launch room-member-access --file FILE
|
|
56
|
+
codiscuss launch room-member-adapter --file FILE
|
|
56
57
|
codiscuss launch room-profiles --file FILE
|
|
57
58
|
codiscuss launch room-restore --file FILE
|
|
58
59
|
codiscuss launch pick-directory
|
|
@@ -62,7 +63,7 @@ const HELP = `用法:codiscuss launch settings [--file FILE]
|
|
|
62
63
|
settings 读取本机软件默认配置;指定文件时保存,不启动 AI。
|
|
63
64
|
start 使用启动 JSON 创建或接入房间并启动;--dry-run 只校验和预览。
|
|
64
65
|
room-config 修正已暂停房间的软件路径或切换 Codex App / CLI,不启动讨论;须提供 site、room_id、token。
|
|
65
|
-
room-models 只读指定 site、room_id
|
|
66
|
+
room-models 只读指定 site、room_id 的本机登记模型与思考强度,不启动讨论;quick 为 true 时不查询 agy 的默认模型(较慢),该成员标 defaults_pending。
|
|
66
67
|
models 按 adapter、executable 或 API profile 读取模型目录,不启动讨论或生成回答。
|
|
67
68
|
api-key 将 base_url、api_key 保存于本机私有目录,只返回 api_key_ref。
|
|
68
69
|
api-check 用 API profile 向模型发送一次极短请求,核对地址、密钥、协议与模型是否可用。
|
|
@@ -72,6 +73,7 @@ projects 读取本机已配置的项目(名称与绝对路径);提供 JSON
|
|
|
72
73
|
machine-link 返回本机连接指定 site 的电脑编号、名称与凭据,供已登录管理员的网页完成配对;不存在时创建。
|
|
73
74
|
room-member 为运行中的自由房间或讨论邀请、请回一位成员(讨论只接受本机唤醒的成员);须提供 site、room_id、token、action(add/rejoin)、name。
|
|
74
75
|
room-member-access 修改一位本机唤醒成员的项目文件权限(project_access 为 read/write),下次投递起生效、保留原会话;须提供 site、room_id、token、name、project_access。
|
|
76
|
+
room-member-adapter 把自由房间里空闲的 Codex 成员在 App(codex)与 CLI(codex_cli)间切换,保留原会话;须提供 site、room_id、token、name、adapter。
|
|
75
77
|
room-profiles 导出房间的本机成员软件配置与实际会话 ID(不含凭据),供备份附带;须提供 site、room_id、token。
|
|
76
78
|
room-restore 把导入房间的本机成员配置登记到本机并核对原会话存在,Claude Code 成员保持手动;须提供 site、room_id、token、local_members。
|
|
77
79
|
JSON 指定 site、entry(conversation/web)、room(建房内容,含 submission_id)
|
|
@@ -242,7 +244,10 @@ export function launch_settings(home) {
|
|
|
242
244
|
return existsSync(path) ? JSON.parse(readFileSync(path, 'utf8')) : { participants: {} };
|
|
243
245
|
}
|
|
244
246
|
|
|
245
|
-
/**
|
|
247
|
+
/**
|
|
248
|
+
* Read the registered room's model choices, what each software selects for empty ones, and its smart scheduler, never connection secrets.
|
|
249
|
+
* With `quick`, agy's default model is not queried and its entry is marked `defaults_pending`.
|
|
250
|
+
*/
|
|
246
251
|
export async function room_models(home, raw, { read_defaults = software_defaults } = {}) {
|
|
247
252
|
const input = launch_input({ ...raw, entry: 'web' });
|
|
248
253
|
if (!input.room_id) throw new Error('读取本场模型须提供 room_id');
|
|
@@ -255,10 +260,14 @@ export async function room_models(home, raw, { read_defaults = software_defaults
|
|
|
255
260
|
const profiles = config.participants || {};
|
|
256
261
|
const participants = Object.fromEntries(await Promise.all(Object.entries(profiles).map(async ([name, profile]) => {
|
|
257
262
|
const cli = ['codex_cli', 'claude', 'antigravity_cli', 'api'].includes(profile.adapter);
|
|
258
|
-
|
|
259
|
-
|
|
263
|
+
// agy has no settings file and is run to name its default model, which takes seconds; a quick read leaves that to a later full read.
|
|
264
|
+
const pending = input.quick === true && profile.adapter === 'antigravity_cli';
|
|
265
|
+
const defaults = pending ? { model: '', effort: '' } : await read_defaults(profile);
|
|
266
|
+
return [name, { ...(pending ? { defaults_pending: true } : {}), adapter: profile.adapter, model: cli ? profile.model || '' : '',
|
|
260
267
|
effort: EFFORT_LEVELS[profile.adapter] ? profile.effort || '' : '', default_model: defaults.model, default_effort: defaults.effort,
|
|
261
|
-
project_access: profile.adapter === 'api' ? 'read' : profile.project_access || config.project_access || 'read'
|
|
268
|
+
project_access: profile.adapter === 'api' ? 'read' : profile.project_access || config.project_access || 'read',
|
|
269
|
+
// An agy member resuming its own conversation keeps that conversation's project, so this tool cannot set its access.
|
|
270
|
+
own_project: profile.adapter === 'antigravity_cli' && Boolean(profile.session_id) }];
|
|
262
271
|
})));
|
|
263
272
|
// Smart scheduling is a local choice; only which model decides is shown, never its key.
|
|
264
273
|
const scheduler = config.scheduler ? { provider: config.scheduler.provider, model: config.scheduler.model } : null;
|
|
@@ -578,6 +587,7 @@ export async function room_member_access(home, raw) {
|
|
|
578
587
|
const profile = config.participants?.[name];
|
|
579
588
|
if (!profile) throw new Error(`${name} 不由本机唤醒,项目权限由它自己的会话决定`);
|
|
580
589
|
if (!ACCESS_ADAPTERS.includes(profile.adapter)) throw new Error(profile.adapter === 'api' ? 'API 成员没有本机文件工具,项目固定只读' : `${name} 的项目权限在 App 内设置`);
|
|
590
|
+
if (profile.adapter === 'antigravity_cli' && profile.session_id) throw new Error(`${name} 续接的是它自己的 agy 对话,项目权限沿用该对话所在项目,请在 agy 中设置`);
|
|
581
591
|
if (project_access === 'write' && !(profile.cwd || config.cwd)) throw new Error('本房间没有指定项目目录,可写没有作用;请在新建房间时选择项目目录');
|
|
582
592
|
const next = { ...config, participants: { ...config.participants, [name]: { ...profile, project_access } } };
|
|
583
593
|
validate_config(next, dirname(path));
|
|
@@ -586,6 +596,36 @@ export async function room_member_access(home, raw) {
|
|
|
586
596
|
} finally { release(); }
|
|
587
597
|
}
|
|
588
598
|
|
|
599
|
+
/**
|
|
600
|
+
* Switch an idle free-room Codex member between the App and the CLI, keeping its conversation: the App resumes the
|
|
601
|
+
* bound chat by its session, and the CLI resumes it only after the App has released it. Model and effort are chosen
|
|
602
|
+
* in the App, so switching to it drops them.
|
|
603
|
+
*/
|
|
604
|
+
export async function room_member_adapter(home, raw) {
|
|
605
|
+
const { input, path, config: registered } = await member_room_context(home, raw);
|
|
606
|
+
const { name, adapter } = input;
|
|
607
|
+
if (registered.mode !== 'free') throw new Error('讨论进行中不能切换;结束讨论后再切换,或暂停后在“接入”页修正');
|
|
608
|
+
if (!CODEX_ADAPTERS.includes(adapter)) throw new Error('adapter 只能为 codex(App)或 codex_cli(CLI)');
|
|
609
|
+
const room = await site_request(input.site, `/api/rooms/${input.room_id}`, input.token);
|
|
610
|
+
const member = room.free.members.find(item => item.name === name);
|
|
611
|
+
if (member?.pending > 0 || ['dispatched', 'started'].includes(member?.execution?.status)) throw new Error(`${name} 正在处理请求,请等它完成或先停止`);
|
|
612
|
+
const release = acquire_lock(join(dirname(path), 'configuration-edit'));
|
|
613
|
+
try {
|
|
614
|
+
const config = configuration_snapshot(path).config;
|
|
615
|
+
const profile = config.participants?.[name];
|
|
616
|
+
if (!CODEX_ADAPTERS.includes(profile?.adapter)) throw new Error(`${name} 不是本机唤醒的 Codex 成员`);
|
|
617
|
+
if (profile.adapter === adapter) return { room_id: input.room_id, member: name, adapter };
|
|
618
|
+
const context = { config, path };
|
|
619
|
+
const next = { ...profile };
|
|
620
|
+
switch_codex_adapter(context, name, next, adapter);
|
|
621
|
+
if (adapter === 'codex') { delete next.model; delete next.effort; }
|
|
622
|
+
const updated = { ...config, participants: { ...config.participants, [name]: next } };
|
|
623
|
+
validate_config(updated, dirname(path));
|
|
624
|
+
atomic_json(path, updated);
|
|
625
|
+
return { room_id: input.room_id, member: name, adapter };
|
|
626
|
+
} finally { release(); }
|
|
627
|
+
}
|
|
628
|
+
|
|
589
629
|
/** A Codex desktop chat is resumed through the app, any other Codex session through the CLI. */
|
|
590
630
|
function codex_session_adapter(session_id, env = process.env) {
|
|
591
631
|
const sessions = join(env.CODEX_HOME || join(homedir(), '.codex'), 'sessions');
|
|
@@ -1022,6 +1062,7 @@ export async function launch_main(args = process.argv.slice(2), input_text) {
|
|
|
1022
1062
|
}
|
|
1023
1063
|
else if (positionals[0] === 'room-member' && input) result = await room_member(home, input);
|
|
1024
1064
|
else if (positionals[0] === 'room-member-access' && input) result = await room_member_access(home, input);
|
|
1065
|
+
else if (positionals[0] === 'room-member-adapter' && input) result = await room_member_adapter(home, input);
|
|
1025
1066
|
else if (positionals[0] === 'room-profiles' && input) result = await room_profiles(home, input);
|
|
1026
1067
|
else if (positionals[0] === 'room-restore' && input) result = await restore_room(home, input);
|
|
1027
1068
|
else if (positionals[0] === 'pick-directory') result = await pick_directory();
|