@timqi/pier 0.1.3 → 0.1.5
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/README.md +12 -7
- package/dist/agent/config.js +9 -3
- package/dist/agent/events.js +13 -5
- package/dist/agent/listing.js +10 -2
- package/dist/agent/packages.js +4 -20
- package/dist/agent/pi.js +18 -86
- package/dist/channels/attach.js +2 -2
- package/dist/channels/commands.js +10 -8
- package/dist/channels/config.js +32 -24
- package/dist/channels/control.js +60 -14
- package/dist/channels/conversations.js +60 -13
- package/dist/channels/handoff.js +90 -0
- package/dist/channels/lark-api.js +7 -0
- package/dist/channels/lark-outbound.js +19 -0
- package/dist/channels/lark-panel.js +41 -26
- package/dist/channels/lark-render.js +2 -1
- package/dist/channels/lark.js +35 -26
- package/dist/channels/lines.js +1 -1
- package/dist/channels/panel.js +324 -115
- package/dist/channels/receipts.js +25 -12
- package/dist/channels/routes.js +21 -7
- package/dist/channels/runtime.js +21 -7
- package/dist/channels/slack-api.js +19 -10
- package/dist/channels/slack-cli.js +503 -0
- package/dist/channels/slack-directory.js +2 -2
- package/dist/channels/slack-outbound.js +11 -1
- package/dist/channels/slack-panel.js +81 -40
- package/dist/channels/slack-render.js +3 -1
- package/dist/channels/slack-thread.js +41 -0
- package/dist/channels/slack-transcript.js +107 -0
- package/dist/channels/slack.js +38 -31
- package/dist/channels/types.js +1 -3
- package/dist/cli.js +166 -7
- package/dist/core/identity.js +61 -17
- package/dist/core/reply.js +19 -15
- package/dist/core/router.js +23 -3
- package/dist/db.js +33 -0
- package/dist/main.js +61 -41
- package/dist/paths.js +3 -0
- package/dist/secrets.js +2 -1
- package/dist/settings.js +3 -12
- package/dist/socket.js +99 -0
- package/dist/tasks/agent.js +5 -6
- package/dist/tasks/callbacks.js +2 -2
- package/dist/tasks/cli.js +225 -0
- package/dist/tasks/definitions.js +3 -3
- package/dist/tasks/execution.js +1 -3
- package/dist/tasks/groups.js +2 -5
- package/dist/tasks/messages.js +18 -158
- package/dist/tasks/operations.js +335 -0
- package/dist/tasks/routes.js +1 -11
- package/dist/tasks/runs.js +5 -20
- package/dist/tasks/service.js +18 -43
- package/dist/tasks/store.js +15 -15
- package/dist/tools.js +18 -3
- package/dist/vault.js +107 -0
- package/dist/web/auth.js +2 -1
- package/dist/web/public/assets/{activity-BSMeRcN2.js → activity-CrybM-E8.js} +2 -2
- package/dist/web/public/assets/activity-CrybM-E8.js.br +0 -0
- package/dist/web/public/assets/activity-CrybM-E8.js.gz +0 -0
- package/dist/web/public/assets/{boards-BCWQMZry.js → boards-Cw7_6J6L.js} +1 -1
- package/dist/web/public/assets/boards-Cw7_6J6L.js.br +0 -0
- package/dist/web/public/assets/boards-Cw7_6J6L.js.gz +0 -0
- package/dist/web/public/assets/{explorer-Cr4XTi4j.js → explorer-DV066dUD.js} +1 -1
- package/dist/web/public/assets/explorer-DV066dUD.js.br +0 -0
- package/dist/web/public/assets/explorer-DV066dUD.js.gz +0 -0
- package/dist/web/public/assets/index-BrNHu2qj.js +85 -0
- package/dist/web/public/assets/index-BrNHu2qj.js.br +0 -0
- package/dist/web/public/assets/index-BrNHu2qj.js.gz +0 -0
- package/dist/web/public/assets/index-DLszkDUV.css +2 -0
- package/dist/web/public/assets/index-DLszkDUV.css.br +0 -0
- package/dist/web/public/assets/index-DLszkDUV.css.gz +0 -0
- package/dist/web/public/assets/{runs-DdERzeac.js → runs-DDagTNaM.js} +1 -1
- package/dist/web/public/assets/runs-DDagTNaM.js.br +0 -0
- package/dist/web/public/assets/runs-DDagTNaM.js.gz +0 -0
- package/dist/web/public/assets/settings-BEdSeXpm.js +5 -0
- package/dist/web/public/assets/settings-BEdSeXpm.js.br +0 -0
- package/dist/web/public/assets/settings-BEdSeXpm.js.gz +0 -0
- package/dist/web/public/assets/{task-runs-C-dGUDsH.js → task-runs-BnMack9t.js} +1 -1
- package/dist/web/public/assets/task-runs-BnMack9t.js.br +0 -0
- package/dist/web/public/assets/task-runs-BnMack9t.js.gz +0 -0
- package/dist/web/public/assets/{tasks-CY30H1u1.js → tasks-BkW7YShZ.js} +1 -1
- package/dist/web/public/assets/tasks-BkW7YShZ.js.br +0 -0
- package/dist/web/public/assets/tasks-BkW7YShZ.js.gz +0 -0
- package/dist/web/public/index.html +2 -2
- package/dist/web/public/index.html.br +0 -0
- package/dist/web/public/index.html.gz +0 -0
- package/dist/web/push.js +4 -10
- package/dist/web/vault.js +68 -0
- package/dist/{extensions/web → websearch}/artifacts.js +2 -2
- package/dist/websearch/cli.js +73 -0
- package/dist/websearch/run.js +273 -0
- package/docs/deploy.md +34 -11
- package/package.json +2 -3
- package/skills/pier-help/SKILL.md +32 -16
- package/skills/pier-slack/SKILL.md +52 -71
- package/skills/pier-tasks/SKILL.md +60 -148
- package/skills/pier-vault/SKILL.md +36 -0
- package/skills/pier-web/SKILL.md +50 -0
- package/dist/channels/slack-tool.js +0 -416
- package/dist/channels/telegram-api.js +0 -86
- package/dist/channels/telegram-panel.js +0 -97
- package/dist/channels/telegram-render.js +0 -69
- package/dist/channels/telegram.js +0 -421
- package/dist/extensions/index.js +0 -10
- package/dist/extensions/web/index.js +0 -9
- package/dist/extensions/web/tools.js +0 -265
- package/dist/tasks/tool.js +0 -416
- package/dist/web/public/assets/activity-BSMeRcN2.js.br +0 -0
- package/dist/web/public/assets/activity-BSMeRcN2.js.gz +0 -0
- package/dist/web/public/assets/boards-BCWQMZry.js.br +0 -0
- package/dist/web/public/assets/boards-BCWQMZry.js.gz +0 -0
- package/dist/web/public/assets/explorer-Cr4XTi4j.js.br +0 -0
- package/dist/web/public/assets/explorer-Cr4XTi4j.js.gz +0 -0
- package/dist/web/public/assets/index-C7tA0Ufu.js +0 -85
- package/dist/web/public/assets/index-C7tA0Ufu.js.br +0 -0
- package/dist/web/public/assets/index-C7tA0Ufu.js.gz +0 -0
- package/dist/web/public/assets/index-DVIt5Gio.css +0 -2
- package/dist/web/public/assets/index-DVIt5Gio.css.br +0 -0
- package/dist/web/public/assets/index-DVIt5Gio.css.gz +0 -0
- package/dist/web/public/assets/runs-DdERzeac.js.br +0 -0
- package/dist/web/public/assets/runs-DdERzeac.js.gz +0 -0
- package/dist/web/public/assets/settings-CQDAHoMM.js +0 -5
- package/dist/web/public/assets/settings-CQDAHoMM.js.br +0 -0
- package/dist/web/public/assets/settings-CQDAHoMM.js.gz +0 -0
- package/dist/web/public/assets/task-runs-C-dGUDsH.js.br +0 -0
- package/dist/web/public/assets/task-runs-C-dGUDsH.js.gz +0 -0
- package/dist/web/public/assets/tasks-CY30H1u1.js.br +0 -0
- package/dist/web/public/assets/tasks-CY30H1u1.js.gz +0 -0
- /package/dist/{extensions/web → websearch}/anthropic.js +0 -0
- /package/dist/{extensions/web → websearch}/content.js +0 -0
- /package/dist/{extensions/web → websearch}/http.js +0 -0
- /package/dist/{extensions/web → websearch}/json.js +0 -0
- /package/dist/{extensions/web → websearch}/language.js +0 -0
- /package/dist/{extensions/web → websearch}/openai.js +0 -0
- /package/dist/{extensions/web → websearch}/provider.js +0 -0
package/dist/channels/routes.js
CHANGED
|
@@ -1,5 +1,7 @@
|
|
|
1
|
-
// Settings → Channels HTTP surface: one document per platform
|
|
1
|
+
// Settings → Channels HTTP surface: one document per platform, and the
|
|
2
|
+
// web → IM handoff beside it.
|
|
2
3
|
import { isThinkingLevel } from "../core/types.js";
|
|
4
|
+
import { HandoffError } from "./handoff.js";
|
|
3
5
|
import { defaultChannelConfig, isChannelPlatform, } from "./types.js";
|
|
4
6
|
/** Stable mask: recomputable at write time, so "unchanged" is detectable. */
|
|
5
7
|
const maskToken = (token) => token ? `${"•".repeat(8)}${token.slice(-4)}` : "";
|
|
@@ -35,14 +37,13 @@ function parseChats(raw, known) {
|
|
|
35
37
|
enabled: asBool(edit.enabled),
|
|
36
38
|
requireMention: asBool(edit.requireMention),
|
|
37
39
|
requireBind: asBool(edit.requireBind),
|
|
38
|
-
topicMode: asBool(edit.topicMode),
|
|
39
40
|
cwd: asString(edit.cwd),
|
|
40
41
|
model: asModel(edit.model),
|
|
41
42
|
thinking: asThinking(edit.thinking),
|
|
42
43
|
};
|
|
43
44
|
});
|
|
44
45
|
}
|
|
45
|
-
export function registerChannelRoutes(app, store, runtime) {
|
|
46
|
+
export function registerChannelRoutes(app, store, runtime, handoff) {
|
|
46
47
|
app.get("/api/channels/:platform", (c) => {
|
|
47
48
|
const platform = c.req.param("platform");
|
|
48
49
|
if (!isChannelPlatform(platform))
|
|
@@ -70,12 +71,8 @@ export function registerChannelRoutes(app, store, runtime) {
|
|
|
70
71
|
enabled: asBool(body.enabled),
|
|
71
72
|
token: kept(asString(body.token), current.token),
|
|
72
73
|
appToken: kept(asString(body.appToken), current.appToken),
|
|
73
|
-
// Absent means "on": a client that predates the field must not silently
|
|
74
|
-
// switch off a capability the operator never touched.
|
|
75
|
-
agentTool: body.agentTool === undefined ? current.agentTool : asBool(body.agentTool),
|
|
76
74
|
requireMention: asBool(body.requireMention),
|
|
77
75
|
requireBind: asBool(body.requireBind),
|
|
78
|
-
topicMode: asBool(body.topicMode),
|
|
79
76
|
cwd: asString(body.cwd),
|
|
80
77
|
model: asModel(body.model),
|
|
81
78
|
thinking: asThinking(body.thinking),
|
|
@@ -100,4 +97,21 @@ export function registerChannelRoutes(app, store, runtime) {
|
|
|
100
97
|
store.unbind(platform, c.req.param("id"));
|
|
101
98
|
return c.json({ ok: true });
|
|
102
99
|
});
|
|
100
|
+
app.get("/api/handoff/targets", (c) => c.json({ targets: handoff.targets() }));
|
|
101
|
+
app.post("/api/handoff", async (c) => {
|
|
102
|
+
const body = (await c.req.json().catch(() => null));
|
|
103
|
+
const sessionId = asString(body?.sessionId);
|
|
104
|
+
const chatId = asString(body?.chatId);
|
|
105
|
+
const platform = body?.platform;
|
|
106
|
+
if (!sessionId || !chatId || !isChannelPlatform(platform))
|
|
107
|
+
return c.json({ error: "invalid body" }, 400);
|
|
108
|
+
try {
|
|
109
|
+
return c.json(await handoff.continueIn({ sessionId, platform, chatId }), 201);
|
|
110
|
+
}
|
|
111
|
+
catch (err) {
|
|
112
|
+
if (err instanceof HandoffError)
|
|
113
|
+
return c.json({ error: err.message }, err.status);
|
|
114
|
+
throw err;
|
|
115
|
+
}
|
|
116
|
+
});
|
|
103
117
|
}
|
package/dist/channels/runtime.js
CHANGED
|
@@ -3,12 +3,10 @@
|
|
|
3
3
|
import { logger } from "../log.js";
|
|
4
4
|
import { LarkChannel } from "./lark.js";
|
|
5
5
|
import { SlackChannel } from "./slack.js";
|
|
6
|
-
import { TelegramChannel } from "./telegram.js";
|
|
7
6
|
const ADAPTERS = [
|
|
8
|
-
{ platform: "
|
|
9
|
-
{ platform: "slack", needsAppToken: true, build: (deps) => new SlackChannel(deps) },
|
|
7
|
+
{ platform: "slack", build: (deps) => new SlackChannel(deps) },
|
|
10
8
|
// Lark's "token" is the App ID and "appToken" the App Secret.
|
|
11
|
-
{ platform: "lark",
|
|
9
|
+
{ platform: "lark", build: (deps) => new LarkChannel(deps) },
|
|
12
10
|
];
|
|
13
11
|
// The injected sink is for warnings; "slack started" is not one.
|
|
14
12
|
const log = logger("channels");
|
|
@@ -18,14 +16,18 @@ export class ChannelRuntime {
|
|
|
18
16
|
store;
|
|
19
17
|
router;
|
|
20
18
|
control;
|
|
19
|
+
handoff;
|
|
21
20
|
log;
|
|
22
21
|
live = new Map();
|
|
23
22
|
reloading = Promise.resolve();
|
|
24
23
|
stopped = false;
|
|
25
|
-
constructor(store, router, control,
|
|
24
|
+
constructor(store, router, control,
|
|
25
|
+
/** The pull half only; the push half needs this runtime, so main.ts closes the loop. */
|
|
26
|
+
handoff, log = warn) {
|
|
26
27
|
this.store = store;
|
|
27
28
|
this.router = router;
|
|
28
29
|
this.control = control;
|
|
30
|
+
this.handoff = handoff;
|
|
29
31
|
this.log = log;
|
|
30
32
|
}
|
|
31
33
|
/** Serialized: two concurrent Console saves would race into duplicate live
|
|
@@ -45,7 +47,7 @@ export class ChannelRuntime {
|
|
|
45
47
|
return run;
|
|
46
48
|
}
|
|
47
49
|
async restart(adapter) {
|
|
48
|
-
const { platform,
|
|
50
|
+
const { platform, build } = adapter;
|
|
49
51
|
const existing = this.live.get(platform);
|
|
50
52
|
if (existing) {
|
|
51
53
|
this.live.delete(platform);
|
|
@@ -55,7 +57,7 @@ export class ChannelRuntime {
|
|
|
55
57
|
const config = this.store.get(platform);
|
|
56
58
|
if (!config.enabled || !config.token)
|
|
57
59
|
return;
|
|
58
|
-
if (
|
|
60
|
+
if (!config.appToken) {
|
|
59
61
|
// "Enabled but nothing happens" is indistinguishable from a broken adapter.
|
|
60
62
|
this.log(`${platform}: enabled but no app token, not starting`);
|
|
61
63
|
return;
|
|
@@ -64,6 +66,7 @@ export class ChannelRuntime {
|
|
|
64
66
|
store: this.store,
|
|
65
67
|
log: (m) => this.log(`${platform}: ${m}`),
|
|
66
68
|
control: this.control,
|
|
69
|
+
handoff: this.handoff,
|
|
67
70
|
});
|
|
68
71
|
try {
|
|
69
72
|
await channel.start((msg) => {
|
|
@@ -87,6 +90,17 @@ export class ChannelRuntime {
|
|
|
87
90
|
await channel.notify(conversationId, { text, origin: { kind: "error" } });
|
|
88
91
|
return true;
|
|
89
92
|
}
|
|
93
|
+
running() {
|
|
94
|
+
return [...this.live.keys()];
|
|
95
|
+
}
|
|
96
|
+
/** Throws by name when the platform is not running: the caller's answer is
|
|
97
|
+
* "enable it in Settings", not a silent no-op. */
|
|
98
|
+
async openThread(platform, chatId, note) {
|
|
99
|
+
const channel = this.live.get(platform);
|
|
100
|
+
if (!channel)
|
|
101
|
+
throw new Error(`${platform} is not running`);
|
|
102
|
+
return channel.openThread(chatId, note);
|
|
103
|
+
}
|
|
90
104
|
async stop() {
|
|
91
105
|
// An in-flight restart could otherwise register an adapter after `live`
|
|
92
106
|
// was cleared — running, unstoppable.
|
|
@@ -21,18 +21,24 @@ export class SlackApi {
|
|
|
21
21
|
appToken;
|
|
22
22
|
log;
|
|
23
23
|
openSocket;
|
|
24
|
+
retries;
|
|
24
25
|
socketRunning = false;
|
|
25
26
|
constructor(token, appToken, log = () => { },
|
|
26
27
|
/** Injected in tests. */
|
|
27
|
-
openSocket = (url) => new WebSocket(url)
|
|
28
|
+
openSocket = (url) => new WebSocket(url),
|
|
29
|
+
/** How many rate-limit waits one call sits through. The adapter takes one:
|
|
30
|
+
* a turn must not hang for minutes. A batch process (`pier slack`) takes more. */
|
|
31
|
+
retries = 1) {
|
|
28
32
|
this.token = token;
|
|
29
33
|
this.appToken = appToken;
|
|
30
34
|
this.log = log;
|
|
31
35
|
this.openSocket = openSocket;
|
|
36
|
+
this.retries = retries;
|
|
32
37
|
}
|
|
33
38
|
/** Slack accepts a JSON body only on write methods; a read method silently
|
|
34
39
|
* ignores it and reports the parameter missing (`users.info` answers
|
|
35
|
-
* `user_not_found`). So reads go form-encoded.
|
|
40
|
+
* `user_not_found`). So reads go form-encoded. Public for the long tail of
|
|
41
|
+
* read methods `pier slack` needs and the adapter never calls. */
|
|
36
42
|
async read(method, params) {
|
|
37
43
|
const form = new URLSearchParams();
|
|
38
44
|
for (const [key, value] of Object.entries(params)) {
|
|
@@ -41,7 +47,7 @@ export class SlackApi {
|
|
|
41
47
|
}
|
|
42
48
|
return this.call(method, form);
|
|
43
49
|
}
|
|
44
|
-
async call(method, payload, token = this.token,
|
|
50
|
+
async call(method, payload, token = this.token, tries = this.retries) {
|
|
45
51
|
const form = payload instanceof URLSearchParams;
|
|
46
52
|
const res = await fetch(`${BASE}/${method}`, {
|
|
47
53
|
method: "POST",
|
|
@@ -54,16 +60,17 @@ export class SlackApi {
|
|
|
54
60
|
body: form ? payload.toString() : JSON.stringify(payload),
|
|
55
61
|
signal: AbortSignal.timeout(30_000),
|
|
56
62
|
});
|
|
63
|
+
const body = (await res.json().catch(() => null));
|
|
57
64
|
// A long turn split into chunks hits ~1 msg/s per channel; the header
|
|
58
|
-
// carries the exact wait.
|
|
59
|
-
|
|
65
|
+
// carries the exact wait. Some methods say `ratelimited` in a 200 body
|
|
66
|
+
// instead. Obeyed `retries` times; the next one throws.
|
|
67
|
+
if ((res.status === 429 || body?.error === "ratelimited") && tries > 0) {
|
|
60
68
|
const after = Number(res.headers.get("retry-after") ?? "1");
|
|
61
69
|
if (Number.isFinite(after) && after <= 60) {
|
|
62
70
|
await new Promise((r) => setTimeout(r, (after + 1) * 1000));
|
|
63
|
-
return this.call(method, payload, token,
|
|
71
|
+
return this.call(method, payload, token, tries - 1);
|
|
64
72
|
}
|
|
65
73
|
}
|
|
66
|
-
const body = (await res.json().catch(() => null));
|
|
67
74
|
if (!body)
|
|
68
75
|
throw new Error(`slack ${method}: ${res.status} with no JSON body`);
|
|
69
76
|
if (!body.ok)
|
|
@@ -259,7 +266,7 @@ export class SlackApi {
|
|
|
259
266
|
}
|
|
260
267
|
/** Three calls is Slack's current upload (`files.upload` is retired); the
|
|
261
268
|
* upload host is not the Web API and answers plain text. */
|
|
262
|
-
async uploadFile(channel, threadTs, file) {
|
|
269
|
+
async uploadFile(channel, threadTs, file, comment) {
|
|
263
270
|
const slot = await this.read("files.getUploadURLExternal", { filename: file.name, length: file.bytes.length }).catch((err) => {
|
|
264
271
|
// The manifest is only applied when an app is created; name the fix.
|
|
265
272
|
if (!/missing_scope/.test(String(err)))
|
|
@@ -279,10 +286,12 @@ export class SlackApi {
|
|
|
279
286
|
});
|
|
280
287
|
if (!put.ok)
|
|
281
288
|
throw new Error(`slack file upload: ${put.status}`);
|
|
282
|
-
await this.call("files.completeUploadExternal", {
|
|
289
|
+
const done = await this.call("files.completeUploadExternal", {
|
|
283
290
|
files: [{ id: slot.file_id, title: file.name }],
|
|
284
291
|
channel_id: channel,
|
|
285
|
-
thread_ts: threadTs,
|
|
292
|
+
thread_ts: threadTs || undefined,
|
|
293
|
+
initial_comment: comment,
|
|
286
294
|
});
|
|
295
|
+
return { id: done.files?.[0]?.id ?? slot.file_id };
|
|
287
296
|
}
|
|
288
297
|
}
|