@evandu/super-pi 0.1.132 → 0.1.135
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/dist/.super-pi-check-inputs.json +4 -0
- package/dist/client/.super-pi-client-sources.json +8 -8
- package/dist/client/assets/{CodeMirrorEditor-ktyL2PcX.js → CodeMirrorEditor-D6awRUZO.js} +2 -2
- package/dist/client/assets/{dist--igDAzRS.js → dist-1jSyapdW.js} +1 -1
- package/dist/client/assets/{dist-CPLCQDqy.js → dist-BGgidsaF.js} +1 -1
- package/dist/client/assets/{dist-DNZMXYNq.js → dist-BYMYC_2I.js} +1 -1
- package/dist/client/assets/{dist-vsMBLoxV.js → dist-BkVaPA_q.js} +1 -1
- package/dist/client/assets/{dist-B8OZ-n_C.js → dist-BoeM5xW7.js} +1 -1
- package/dist/client/assets/{dist-D_Zdnhm8.js → dist-C46474ys.js} +1 -1
- package/dist/client/assets/{dist-Cu75NyQ0.js → dist-CCkdCH6a.js} +1 -1
- package/dist/client/assets/{dist-BJi5HmH4.js → dist-CNuKauOq.js} +1 -1
- package/dist/client/assets/{dist-BAJCrbnX.js → dist-CubYGjAk.js} +1 -1
- package/dist/client/assets/{dist-8pdzh6h1.js → dist-Cwb-Lb5-.js} +1 -1
- package/dist/client/assets/{dist-BRjGckmQ.js → dist-D0Iw5XQA.js} +1 -1
- package/dist/client/assets/{dist-BjZUYxLd.js → dist-DCiuVvGU.js} +1 -1
- package/dist/client/assets/{dist-Hj_dWgPr.js → dist-DHBWUQhM.js} +1 -1
- package/dist/client/assets/{dist-CoszOk5k.js → dist-DOwrSiuY.js} +1 -1
- package/dist/client/assets/{dist-Bg9zLfex.js → dist-DeZsNbFx.js} +1 -1
- package/dist/client/assets/{dist-C6iAHGvb.js → dist-Dk3e6RfY.js} +1 -1
- package/dist/client/assets/{dist-Bv6P2Rcp.js → dist-DoDmoWTn.js} +1 -1
- package/dist/client/assets/{dist-CgmISAMr.js → dist-WkVUFX28.js} +1 -1
- package/dist/client/assets/{dist-DvHghuGd.js → dist-lZUWPQbT.js} +1 -1
- package/dist/client/assets/{dist-Zhdkfvng.js → dist-mPR8uuzk.js} +1 -1
- package/dist/client/assets/{dist-60usx__o.js → dist-spB2Q2CG.js} +1 -1
- package/dist/client/assets/{dist-CMscy2Nn.js → dist-yTTCht0J.js} +1 -1
- package/dist/client/assets/index-Dyta-HkQ.js +100 -0
- package/dist/client/assets/{index-D3MEG8uH.css → index-V93Kzott.css} +1 -1
- package/dist/client/index.html +2 -2
- package/dist/server/active-memory.d.ts +1 -0
- package/dist/server/active-memory.js +6 -2
- package/dist/server/agents.js +21 -1
- package/dist/server/application/feishu-service.d.ts +57 -10
- package/dist/server/application/feishu-service.js +118 -24
- package/dist/server/application/run-service.d.ts +4 -0
- package/dist/server/application/run-service.js +123 -88
- package/dist/server/application/workspace-service.d.ts +8 -4
- package/dist/server/application/workspace-service.js +11 -10
- package/dist/server/failure-text.d.ts +18 -0
- package/dist/server/failure-text.js +44 -0
- package/dist/server/feishu-cli.d.ts +18 -2
- package/dist/server/feishu-cli.js +46 -12
- package/dist/server/feishu-config.d.ts +68 -0
- package/dist/server/feishu-config.js +84 -0
- package/dist/server/feishu.d.ts +187 -0
- package/dist/server/feishu.js +695 -0
- package/dist/server/http-server.js +36 -5
- package/dist/server/index.js +5 -2
- package/dist/server/knowledge-embedding-worker.d.ts +1 -0
- package/dist/server/knowledge-embedding-worker.js +27 -0
- package/dist/server/knowledge-vectors.d.ts +5 -3
- package/dist/server/knowledge-vectors.js +49 -47
- package/dist/server/knowledge.d.ts +1 -0
- package/dist/server/knowledge.js +6 -2
- package/dist/server/persistence/sqlite-state-repository.d.ts +0 -1
- package/dist/server/persistence/sqlite-state-repository.js +55 -1
- package/dist/server/pi-session-events.js +10 -2
- package/dist/server/pi-session-pool.d.ts +33 -1
- package/dist/server/pi-session-pool.js +20 -2
- package/dist/server/providers.d.ts +13 -0
- package/dist/server/providers.js +27 -2
- package/dist/server/runtime.d.ts +26 -1
- package/dist/server/runtime.js +98 -9
- package/dist/server/store.d.ts +61 -0
- package/dist/server/store.js +112 -14
- package/dist/server/types.d.ts +10 -0
- package/dist/server/ws-server.d.ts +3 -0
- package/dist/server/ws-server.js +35 -6
- package/package.json +25 -2
- package/dist/client/assets/index-ChyemF2I.js +0 -100
package/dist/client/index.html
CHANGED
|
@@ -8,8 +8,8 @@
|
|
|
8
8
|
<link rel="alternate icon" href="/favicon.ico" />
|
|
9
9
|
<link rel="apple-touch-icon" href="/favicon.svg" />
|
|
10
10
|
<title>Super Pi</title>
|
|
11
|
-
<script type="module" crossorigin src="/assets/index-
|
|
12
|
-
<link rel="stylesheet" crossorigin href="/assets/index-
|
|
11
|
+
<script type="module" crossorigin src="/assets/index-Dyta-HkQ.js"></script>
|
|
12
|
+
<link rel="stylesheet" crossorigin href="/assets/index-V93Kzott.css">
|
|
13
13
|
</head>
|
|
14
14
|
<body>
|
|
15
15
|
<div id="root"></div>
|
|
@@ -109,10 +109,14 @@ export class ActiveMemory {
|
|
|
109
109
|
release(token) { this.capabilities.delete(token); }
|
|
110
110
|
identity(token) {
|
|
111
111
|
const identity = this.capabilities.get(token);
|
|
112
|
-
if (!identity || !this.
|
|
112
|
+
if (!identity || !this.hasWorkspace(identity.workspaceId))
|
|
113
113
|
throw new Error("记忆授权已失效");
|
|
114
114
|
return identity;
|
|
115
115
|
}
|
|
116
|
+
hasWorkspace(workspaceId) {
|
|
117
|
+
const store = this.store;
|
|
118
|
+
return store.hasWorkspace ? store.hasWorkspace(workspaceId) : store.snapshot().workspaces.some(w => w.id === workspaceId);
|
|
119
|
+
}
|
|
116
120
|
search(workspaceId, query, sessionId) {
|
|
117
121
|
const words = query.toLocaleLowerCase().match(/[\p{L}\p{N}_-]{2,}/gu)?.slice(0, 12) ?? [];
|
|
118
122
|
if (!words.length)
|
|
@@ -232,7 +236,7 @@ export class ActiveMemory {
|
|
|
232
236
|
return alreadyLocked ? work() : this.locked(work);
|
|
233
237
|
}
|
|
234
238
|
async manage(workspaceId, body) {
|
|
235
|
-
if (!this.
|
|
239
|
+
if (!this.hasWorkspace(workspaceId))
|
|
236
240
|
throw new Error("工作区不存在");
|
|
237
241
|
if (body.action === "configure") {
|
|
238
242
|
await this.configure(body.settings);
|
package/dist/server/agents.js
CHANGED
|
@@ -447,7 +447,27 @@ export async function readAgents(directory, options = {}) {
|
|
|
447
447
|
throw error;
|
|
448
448
|
}
|
|
449
449
|
for (const file of entries.filter((name) => name.toLowerCase().endsWith(".md")).sort()) {
|
|
450
|
-
|
|
450
|
+
// A FILE THAT CANNOT BE READ IS THE SAME KIND OF PROBLEM AS ONE THAT CANNOT BE PARSED.
|
|
451
|
+
//
|
|
452
|
+
// The function's own reason for existing says it plainly: "one typo in one agent must not take
|
|
453
|
+
// down the picker and hide every other agent with it". A file that cannot be READ is at least as
|
|
454
|
+
// ordinary as that - on Windows "another program has this file open" is a Tuesday, not an
|
|
455
|
+
// emergency - and it used to be the one unguarded step in this loop: the parse failure below is
|
|
456
|
+
// collected as a problem, while the read failure was thrown, so a single locked file emptied the
|
|
457
|
+
// whole directory. Four callers ate that: the main agent's own definition (the turn could not
|
|
458
|
+
// start), the roster handed to sub-agents on every turn, the roster used when dispatching one,
|
|
459
|
+
// and the picker (which came back with an empty agent list).
|
|
460
|
+
let source;
|
|
461
|
+
try {
|
|
462
|
+
source = await readFile(join(dir, file), "utf8");
|
|
463
|
+
}
|
|
464
|
+
catch (error) {
|
|
465
|
+
const fileId = file.replace(/\.md$/i, "");
|
|
466
|
+
if (!seenIds.has(fileId)) {
|
|
467
|
+
problems.push({ file, message: `读取失败:${error instanceof Error ? error.message : String(error)}` });
|
|
468
|
+
}
|
|
469
|
+
continue;
|
|
470
|
+
}
|
|
451
471
|
const parsed = parseDefinition(file, source);
|
|
452
472
|
if ("message" in parsed) {
|
|
453
473
|
const fileId = file.replace(/\.md$/i, "");
|
|
@@ -23,8 +23,31 @@ export type FeishuStatus = {
|
|
|
23
23
|
identity?: "user" | "bot";
|
|
24
24
|
/** Whether the agent can read and write documents right now. */
|
|
25
25
|
connected: boolean;
|
|
26
|
+
/**
|
|
27
|
+
* WHICH STEP OF THE GUIDE THE PERSON IS ON.
|
|
28
|
+
*
|
|
29
|
+
* 1 = install the CLI, 2 = connect the account, 3 = ready. The panel draws the three steps and
|
|
30
|
+
* marks this one, so "what do I do now" is answered by where the cursor is rather than by a
|
|
31
|
+
* sentence the person has to interpret.
|
|
32
|
+
*/
|
|
33
|
+
step: 1 | 2 | 3;
|
|
26
34
|
/** What to do next, in one sentence. */
|
|
27
35
|
message: string;
|
|
36
|
+
/** The CLI's own words, when it had something to say about the situation. */
|
|
37
|
+
detail?: string;
|
|
38
|
+
/** The CLI's own hint, which usually names the exact command to run. */
|
|
39
|
+
hint?: string;
|
|
40
|
+
/** Whether the check actually reached Feishu. */
|
|
41
|
+
verified?: boolean;
|
|
42
|
+
/**
|
|
43
|
+
* WHETHER THE APPLICATION ITSELF HAS TO BE REBUILT.
|
|
44
|
+
*
|
|
45
|
+
* Distinct from "step 2": a person whose application is fine but who has not logged in still
|
|
46
|
+
* needs step 2, while a person whose credential was refused needs a NEW application. Telling
|
|
47
|
+
* those two apart is what keeps a transient network failure from being answered by "let me
|
|
48
|
+
* create you another application".
|
|
49
|
+
*/
|
|
50
|
+
rebuild?: boolean;
|
|
28
51
|
};
|
|
29
52
|
/** The outcome of starting the connect flow. */
|
|
30
53
|
export type FeishuConnectResult = {
|
|
@@ -50,35 +73,59 @@ export declare class FeishuService {
|
|
|
50
73
|
private readonly cli;
|
|
51
74
|
/** The device code of a login that is waiting for the person to authorize it. */
|
|
52
75
|
private pendingDeviceCode?;
|
|
76
|
+
/** Whether an application registration is running and waiting for the browser confirmation. */
|
|
77
|
+
private pendingAppCreation;
|
|
53
78
|
constructor(cli?: FeishuCli);
|
|
54
79
|
/**
|
|
55
80
|
* WHAT THE CLI CAN DO RIGHT NOW.
|
|
56
81
|
*
|
|
57
|
-
* The
|
|
82
|
+
* The states a person can be in, and what each one means for the agent:
|
|
58
83
|
*
|
|
59
84
|
* - The CLI is not installed: nothing works, and the fix is one command.
|
|
60
85
|
* - The CLI has no application: `config init --new` creates one through a browser confirmation,
|
|
61
86
|
* so the developer console is never visited.
|
|
62
|
-
* - The
|
|
63
|
-
*
|
|
87
|
+
* - The application exists but its credential is no longer accepted: the CLI says
|
|
88
|
+
* `verify_failed` and names the reason, and the fix is a new application - which is the same
|
|
89
|
+
* browser confirmation, so it stays one button.
|
|
90
|
+
* - The application works but no person is logged in: the agent can act as the application, but
|
|
91
|
+
* it cannot reach that person's own documents until they authorize it.
|
|
64
92
|
*/
|
|
65
93
|
status(): Promise<FeishuStatus>;
|
|
94
|
+
/**
|
|
95
|
+
* CHECKS THE CONNECTION, and says whether it actually works.
|
|
96
|
+
*
|
|
97
|
+
* The status already verifies; this is the same answer shaped for a button, so that a person can
|
|
98
|
+
* ask "it still doesn't work, why?" and get the CLI's own verdict rather than a guess.
|
|
99
|
+
*/
|
|
100
|
+
verify(): Promise<{
|
|
101
|
+
ok: boolean;
|
|
102
|
+
message: string;
|
|
103
|
+
detail?: string;
|
|
104
|
+
hint?: string;
|
|
105
|
+
}>;
|
|
66
106
|
/**
|
|
67
107
|
* STARTS THE CONNECT FLOW.
|
|
68
108
|
*
|
|
69
109
|
* Two steps, and which one runs is decided by what the CLI already has:
|
|
70
110
|
*
|
|
71
|
-
* - No application: `config init --new` registers
|
|
72
|
-
*
|
|
73
|
-
*
|
|
74
|
-
*
|
|
111
|
+
* - No application, or one whose credential no longer works: `config init --new` registers a
|
|
112
|
+
* fresh application through a browser confirmation. It BLOCKS until the person confirms, so it
|
|
113
|
+
* runs in the background and the URL is taken from its output. The broken case goes here too,
|
|
114
|
+
* because a new application is the one repair that needs no secret to be typed into a form.
|
|
115
|
+
* - A working application but no person: `auth login --no-wait --json` asks for a device code
|
|
116
|
+
* and hands back the URL to open. The code is kept here until the person says they are done.
|
|
75
117
|
*/
|
|
76
118
|
connect(): Promise<FeishuConnectResult>;
|
|
77
119
|
/**
|
|
78
|
-
* FINISHES THE
|
|
120
|
+
* FINISHES THE FLOW, once the person says they are done.
|
|
121
|
+
*
|
|
122
|
+
* TWO KINDS OF "DONE", and the difference is what the CLI is waiting for:
|
|
79
123
|
*
|
|
80
|
-
*
|
|
81
|
-
* in the
|
|
124
|
+
* - An application is being registered: `config init --new` finishes by itself once the person
|
|
125
|
+
* confirms in the browser, so there is nothing to hand back. The status is re-read and the
|
|
126
|
+
* answer is whether the application showed up.
|
|
127
|
+
* - A login is being authorized: the device code is handed back to the CLI, which completes the
|
|
128
|
+
* exchange and stores the token in the keychain. Super Pi never sees the token itself.
|
|
82
129
|
*/
|
|
83
130
|
completeConnect(): Promise<FeishuConnectResult>;
|
|
84
131
|
/**
|
|
@@ -17,19 +17,24 @@ export class FeishuService {
|
|
|
17
17
|
cli;
|
|
18
18
|
/** The device code of a login that is waiting for the person to authorize it. */
|
|
19
19
|
pendingDeviceCode;
|
|
20
|
+
/** Whether an application registration is running and waiting for the browser confirmation. */
|
|
21
|
+
pendingAppCreation = false;
|
|
20
22
|
constructor(cli = new FeishuCli()) {
|
|
21
23
|
this.cli = cli;
|
|
22
24
|
}
|
|
23
25
|
/**
|
|
24
26
|
* WHAT THE CLI CAN DO RIGHT NOW.
|
|
25
27
|
*
|
|
26
|
-
* The
|
|
28
|
+
* The states a person can be in, and what each one means for the agent:
|
|
27
29
|
*
|
|
28
30
|
* - The CLI is not installed: nothing works, and the fix is one command.
|
|
29
31
|
* - The CLI has no application: `config init --new` creates one through a browser confirmation,
|
|
30
32
|
* so the developer console is never visited.
|
|
31
|
-
* - The
|
|
32
|
-
*
|
|
33
|
+
* - The application exists but its credential is no longer accepted: the CLI says
|
|
34
|
+
* `verify_failed` and names the reason, and the fix is a new application - which is the same
|
|
35
|
+
* browser confirmation, so it stays one button.
|
|
36
|
+
* - The application works but no person is logged in: the agent can act as the application, but
|
|
37
|
+
* it cannot reach that person's own documents until they authorize it.
|
|
33
38
|
*/
|
|
34
39
|
async status() {
|
|
35
40
|
const status = await this.cli.status();
|
|
@@ -39,59 +44,121 @@ export class FeishuService {
|
|
|
39
44
|
userReady: false,
|
|
40
45
|
botReady: false,
|
|
41
46
|
connected: false,
|
|
42
|
-
|
|
47
|
+
step: 1,
|
|
48
|
+
message: "还没有安装官方飞书 CLI。在终端里跑一次 npm i -g @larksuite/cli 就行,装完回到这里点「连接飞书账号」。",
|
|
49
|
+
...(status.hint ? { hint: status.hint } : {}),
|
|
43
50
|
};
|
|
44
51
|
}
|
|
52
|
+
const common = {
|
|
53
|
+
installed: true,
|
|
54
|
+
...(status.version ? { version: status.version } : {}),
|
|
55
|
+
...(status.appId ? { appId: status.appId } : {}),
|
|
56
|
+
...(status.verified !== undefined ? { verified: status.verified } : {}),
|
|
57
|
+
...(status.note ? { detail: status.note } : {}),
|
|
58
|
+
};
|
|
45
59
|
if (!status.appId) {
|
|
46
60
|
return {
|
|
47
|
-
|
|
48
|
-
|
|
61
|
+
...common,
|
|
62
|
+
userReady: false,
|
|
63
|
+
botReady: false,
|
|
64
|
+
connected: false,
|
|
65
|
+
step: 2,
|
|
66
|
+
message: "CLI 已就绪,还差一个应用。点「连接飞书账号」,浏览器里确认一下就有了——不用去开发者后台注册。",
|
|
67
|
+
};
|
|
68
|
+
}
|
|
69
|
+
// A CREDENTIAL THE CLI KNOWS IS BROKEN is the case a plain status would have hidden: the local
|
|
70
|
+
// file still holds an app, so the panel would have said "已配置", and every document call
|
|
71
|
+
// would have failed. The CLI's own verdict is the one that can be acted on.
|
|
72
|
+
const broken = status.botStatus === "verify_failed" || (status.verified === true && !status.botReady && !status.userReady);
|
|
73
|
+
if (broken) {
|
|
74
|
+
return {
|
|
75
|
+
...common,
|
|
49
76
|
userReady: false,
|
|
50
77
|
botReady: false,
|
|
51
78
|
connected: false,
|
|
52
|
-
|
|
79
|
+
step: 2,
|
|
80
|
+
rebuild: true,
|
|
81
|
+
message: `应用的凭据已经失效:${trimSentence(status.botMessage ?? "飞书拒绝了这次验证")}。点「连接飞书账号」会重新走一遍确认流程、建一个可用的应用。`,
|
|
82
|
+
...(status.hint ? { hint: status.hint } : {}),
|
|
83
|
+
};
|
|
84
|
+
}
|
|
85
|
+
if (status.verified === false) {
|
|
86
|
+
return {
|
|
87
|
+
...common,
|
|
88
|
+
userReady: false,
|
|
89
|
+
botReady: false,
|
|
90
|
+
connected: false,
|
|
91
|
+
step: 2,
|
|
92
|
+
message: "没能连上飞书(本地配置读到了,但验证请求没出去)。检查一下网络,然后点「测试连接」重试。",
|
|
93
|
+
...(status.hint ? { hint: status.hint } : {}),
|
|
53
94
|
};
|
|
54
95
|
}
|
|
55
96
|
if (!status.userReady) {
|
|
56
97
|
return {
|
|
57
|
-
|
|
58
|
-
...(status.version ? { version: status.version } : {}),
|
|
59
|
-
appId: status.appId,
|
|
98
|
+
...common,
|
|
60
99
|
userReady: false,
|
|
61
100
|
botReady: status.botReady,
|
|
62
101
|
connected: false,
|
|
102
|
+
step: 2,
|
|
63
103
|
...(status.identity ? { identity: status.identity } : {}),
|
|
64
|
-
message: "
|
|
104
|
+
message: "应用已经能用,但还没登录你的账号。点「连接飞书账号」在浏览器里授权一下,授权之后 Agent 就能以你的身份读写你自己的文档,不用逐篇加协作者。",
|
|
105
|
+
...(status.hint ? { hint: status.hint } : {}),
|
|
65
106
|
};
|
|
66
107
|
}
|
|
67
108
|
return {
|
|
68
|
-
|
|
69
|
-
...(status.version ? { version: status.version } : {}),
|
|
70
|
-
appId: status.appId,
|
|
109
|
+
...common,
|
|
71
110
|
userReady: true,
|
|
72
111
|
botReady: status.botReady,
|
|
73
112
|
connected: true,
|
|
74
113
|
identity: status.identity ?? "user",
|
|
114
|
+
step: 3,
|
|
75
115
|
message: "已连接:Agent 会以你的身份读写飞书云文档。",
|
|
76
116
|
};
|
|
77
117
|
}
|
|
118
|
+
/**
|
|
119
|
+
* CHECKS THE CONNECTION, and says whether it actually works.
|
|
120
|
+
*
|
|
121
|
+
* The status already verifies; this is the same answer shaped for a button, so that a person can
|
|
122
|
+
* ask "it still doesn't work, why?" and get the CLI's own verdict rather than a guess.
|
|
123
|
+
*/
|
|
124
|
+
async verify() {
|
|
125
|
+
const status = await this.status();
|
|
126
|
+
if (status.connected) {
|
|
127
|
+
return {
|
|
128
|
+
ok: true,
|
|
129
|
+
message: status.message,
|
|
130
|
+
...(status.detail ? { detail: status.detail } : {}),
|
|
131
|
+
};
|
|
132
|
+
}
|
|
133
|
+
return {
|
|
134
|
+
ok: false,
|
|
135
|
+
message: status.message,
|
|
136
|
+
...(status.detail ? { detail: status.detail } : {}),
|
|
137
|
+
...(status.hint ? { hint: status.hint } : {}),
|
|
138
|
+
};
|
|
139
|
+
}
|
|
78
140
|
/**
|
|
79
141
|
* STARTS THE CONNECT FLOW.
|
|
80
142
|
*
|
|
81
143
|
* Two steps, and which one runs is decided by what the CLI already has:
|
|
82
144
|
*
|
|
83
|
-
* - No application: `config init --new` registers
|
|
84
|
-
*
|
|
85
|
-
*
|
|
86
|
-
*
|
|
145
|
+
* - No application, or one whose credential no longer works: `config init --new` registers a
|
|
146
|
+
* fresh application through a browser confirmation. It BLOCKS until the person confirms, so it
|
|
147
|
+
* runs in the background and the URL is taken from its output. The broken case goes here too,
|
|
148
|
+
* because a new application is the one repair that needs no secret to be typed into a form.
|
|
149
|
+
* - A working application but no person: `auth login --no-wait --json` asks for a device code
|
|
150
|
+
* and hands back the URL to open. The code is kept here until the person says they are done.
|
|
87
151
|
*/
|
|
88
152
|
async connect() {
|
|
89
153
|
const status = await this.status();
|
|
90
154
|
if (!status.installed) {
|
|
91
155
|
return { ok: false, message: status.message };
|
|
92
156
|
}
|
|
93
|
-
|
|
157
|
+
// Step 2 is "connect the account"; the application part of it is missing or broken when the
|
|
158
|
+
// person has no application OR when the one they have was refused by Feishu.
|
|
159
|
+
if (!status.appId || status.rebuild) {
|
|
94
160
|
const verificationUri = await this.cli.startAppCreation();
|
|
161
|
+
this.pendingAppCreation = true;
|
|
95
162
|
return {
|
|
96
163
|
ok: true,
|
|
97
164
|
step: "app",
|
|
@@ -119,12 +186,31 @@ export class FeishuService {
|
|
|
119
186
|
}
|
|
120
187
|
}
|
|
121
188
|
/**
|
|
122
|
-
* FINISHES THE
|
|
189
|
+
* FINISHES THE FLOW, once the person says they are done.
|
|
123
190
|
*
|
|
124
|
-
*
|
|
125
|
-
*
|
|
191
|
+
* TWO KINDS OF "DONE", and the difference is what the CLI is waiting for:
|
|
192
|
+
*
|
|
193
|
+
* - An application is being registered: `config init --new` finishes by itself once the person
|
|
194
|
+
* confirms in the browser, so there is nothing to hand back. The status is re-read and the
|
|
195
|
+
* answer is whether the application showed up.
|
|
196
|
+
* - A login is being authorized: the device code is handed back to the CLI, which completes the
|
|
197
|
+
* exchange and stores the token in the keychain. Super Pi never sees the token itself.
|
|
126
198
|
*/
|
|
127
199
|
async completeConnect() {
|
|
200
|
+
if (this.pendingAppCreation) {
|
|
201
|
+
this.pendingAppCreation = false;
|
|
202
|
+
const status = await this.status();
|
|
203
|
+
if (status.appId) {
|
|
204
|
+
return {
|
|
205
|
+
ok: true,
|
|
206
|
+
message: "应用建好了。接下来点「连接飞书账号」授权你的账号,Agent 就能以你的身份读写文档了。",
|
|
207
|
+
};
|
|
208
|
+
}
|
|
209
|
+
return {
|
|
210
|
+
ok: false,
|
|
211
|
+
message: "还没看到应用建好。请确认浏览器里的确认已经提交(有时要等 CLI 写完钥匙串),然后再点一次「我已完成」。",
|
|
212
|
+
};
|
|
213
|
+
}
|
|
128
214
|
if (!this.pendingDeviceCode) {
|
|
129
215
|
return { ok: false, message: "没有待完成的登录。点「连接飞书账号」重新开始。" };
|
|
130
216
|
}
|
|
@@ -235,8 +321,16 @@ export class FeishuService {
|
|
|
235
321
|
throw new Error(`未知的飞书工具操作:${action}`);
|
|
236
322
|
}
|
|
237
323
|
}
|
|
238
|
-
/**
|
|
239
|
-
|
|
324
|
+
/**
|
|
325
|
+
* A SENTENCE WITHOUT ITS OWN FULL STOP.
|
|
326
|
+
*
|
|
327
|
+
* The CLI writes English sentences that end in a period, and this service writes Chinese ones that
|
|
328
|
+
* end in 。 — so a message pasted in verbatim reads "invalid.。点「重新连接」".
|
|
329
|
+
*/
|
|
330
|
+
function trimSentence(text) {
|
|
331
|
+
return text.trim().replace(/[.\s]+$/, "");
|
|
332
|
+
}
|
|
333
|
+
/** The message of a failure, whatever shape the failure has. */ function messageOf(error) {
|
|
240
334
|
if (error instanceof FeishuCliError)
|
|
241
335
|
return error.message;
|
|
242
336
|
if (error instanceof Error)
|
|
@@ -61,6 +61,7 @@ export declare class RunService {
|
|
|
61
61
|
private readonly extensionUiService;
|
|
62
62
|
private agentsDir;
|
|
63
63
|
private readonly workflows;
|
|
64
|
+
private readonly questionWaiters;
|
|
64
65
|
private memory?;
|
|
65
66
|
setMemory(memory: import("../active-memory.js").ActiveMemory): void;
|
|
66
67
|
/**
|
|
@@ -178,6 +179,9 @@ export declare class RunService {
|
|
|
178
179
|
answers?: QuestionAnswer[];
|
|
179
180
|
cancelled?: boolean;
|
|
180
181
|
}): Promise<void>;
|
|
182
|
+
private questionKey;
|
|
183
|
+
private answerPayload;
|
|
184
|
+
private settleQuestion;
|
|
181
185
|
/**
|
|
182
186
|
* THE `question` TOOL BLOCKS HERE, ON THE CALL THAT ASKED IT.
|
|
183
187
|
*
|