@geminixiang/mikan 1.4.0-beta.0 → 1.5.0-beta.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/CHANGELOG.md +43 -3
- package/dist/adapters/commands/desktop.d.ts +5 -0
- package/dist/adapters/commands/desktop.d.ts.map +1 -0
- package/dist/adapters/commands/desktop.js +58 -0
- package/dist/adapters/commands/desktop.js.map +1 -0
- package/dist/adapters/commands/manifest.d.ts.map +1 -1
- package/dist/adapters/commands/manifest.js +8 -0
- package/dist/adapters/commands/manifest.js.map +1 -1
- package/dist/adapters/commands/registry.d.ts.map +1 -1
- package/dist/adapters/commands/registry.js +2 -0
- package/dist/adapters/commands/registry.js.map +1 -1
- package/dist/adapters/commands/types.d.ts +3 -1
- package/dist/adapters/commands/types.d.ts.map +1 -1
- package/dist/adapters/commands/types.js.map +1 -1
- package/dist/adapters/slack/app-config.d.ts +27 -0
- package/dist/adapters/slack/app-config.d.ts.map +1 -0
- package/dist/adapters/slack/app-config.js +241 -0
- package/dist/adapters/slack/app-config.js.map +1 -0
- package/dist/adapters/slack/types.d.ts +46 -1
- package/dist/adapters/slack/types.d.ts.map +1 -1
- package/dist/adapters/slack/types.js.map +1 -1
- package/dist/adapters/web/desktop/index.d.ts +16 -0
- package/dist/adapters/web/desktop/index.d.ts.map +1 -0
- package/dist/adapters/web/desktop/index.js +163 -0
- package/dist/adapters/web/desktop/index.js.map +1 -0
- package/dist/adapters/web/desktop/page.d.ts +2 -0
- package/dist/adapters/web/desktop/page.d.ts.map +1 -0
- package/dist/adapters/web/desktop/page.js +83 -0
- package/dist/adapters/web/desktop/page.js.map +1 -0
- package/dist/adapters/web/desktop/types.d.ts +8 -0
- package/dist/adapters/web/desktop/types.d.ts.map +1 -0
- package/dist/adapters/web/desktop/types.js +2 -0
- package/dist/adapters/web/desktop/types.js.map +1 -0
- package/dist/adapters/web/server.d.ts +2 -0
- package/dist/adapters/web/server.d.ts.map +1 -1
- package/dist/adapters/web/server.js +19 -0
- package/dist/adapters/web/server.js.map +1 -1
- package/dist/cli/boot.d.ts.map +1 -1
- package/dist/cli/boot.js +4 -1
- package/dist/cli/boot.js.map +1 -1
- package/dist/cli/onboard.d.ts +3 -0
- package/dist/cli/onboard.d.ts.map +1 -1
- package/dist/cli/onboard.js +79 -3
- package/dist/cli/onboard.js.map +1 -1
- package/dist/cli/slack.d.ts +9 -0
- package/dist/cli/slack.d.ts.map +1 -0
- package/dist/cli/slack.js +96 -0
- package/dist/cli/slack.js.map +1 -0
- package/dist/cli/types.d.ts +2 -1
- package/dist/cli/types.d.ts.map +1 -1
- package/dist/cli/types.js.map +1 -1
- package/dist/harness/desktop.d.ts +16 -0
- package/dist/harness/desktop.d.ts.map +1 -0
- package/dist/harness/desktop.js +6 -0
- package/dist/harness/desktop.js.map +1 -0
- package/dist/harness/execution-resolver.d.ts +1 -0
- package/dist/harness/execution-resolver.d.ts.map +1 -1
- package/dist/harness/execution-resolver.js +5 -0
- package/dist/harness/execution-resolver.js.map +1 -1
- package/dist/harness/presenter.d.ts.map +1 -1
- package/dist/harness/presenter.js +6 -1
- package/dist/harness/presenter.js.map +1 -1
- package/dist/harness/runner.d.ts.map +1 -1
- package/dist/harness/runner.js +22 -1
- package/dist/harness/runner.js.map +1 -1
- package/dist/harness/session.d.ts +1 -1
- package/dist/harness/session.d.ts.map +1 -1
- package/dist/harness/session.js +30 -9
- package/dist/harness/session.js.map +1 -1
- package/dist/harness/tools/codemode.d.ts.map +1 -1
- package/dist/harness/tools/codemode.js +2 -2
- package/dist/harness/tools/codemode.js.map +1 -1
- package/dist/harness/tools/index.d.ts +2 -2
- package/dist/harness/tools/index.d.ts.map +1 -1
- package/dist/harness/tools/index.js +10 -2
- package/dist/harness/tools/index.js.map +1 -1
- package/dist/harness/tools/jev-browser.d.ts +12 -1
- package/dist/harness/tools/jev-browser.d.ts.map +1 -1
- package/dist/harness/tools/jev-browser.js +189 -30
- package/dist/harness/tools/jev-browser.js.map +1 -1
- package/dist/harness/tools/pi-tools.d.ts.map +1 -1
- package/dist/harness/tools/pi-tools.js +2 -1
- package/dist/harness/tools/pi-tools.js.map +1 -1
- package/dist/harness/tools/types.d.ts +4 -0
- package/dist/harness/tools/types.d.ts.map +1 -1
- package/dist/harness/tools/types.js.map +1 -1
- package/dist/harness/types.d.ts +2 -1
- package/dist/harness/types.d.ts.map +1 -1
- package/dist/harness/types.js.map +1 -1
- package/dist/main.js +29 -0
- package/dist/main.js.map +1 -1
- package/dist/runtime/conversation-runtime.js +7 -0
- package/dist/runtime/conversation-runtime.js.map +1 -1
- package/dist/sandbox/desktop.d.ts +30 -0
- package/dist/sandbox/desktop.d.ts.map +1 -0
- package/dist/sandbox/desktop.js +112 -0
- package/dist/sandbox/desktop.js.map +1 -0
- package/dist/sandbox/provisioner.d.ts +6 -2
- package/dist/sandbox/provisioner.d.ts.map +1 -1
- package/dist/sandbox/provisioner.js +41 -6
- package/dist/sandbox/provisioner.js.map +1 -1
- package/dist/sandbox/types.d.ts +19 -0
- package/dist/sandbox/types.d.ts.map +1 -1
- package/dist/sandbox/types.js.map +1 -1
- package/dist/types.d.ts +17 -1
- package/dist/types.d.ts.map +1 -1
- package/dist/types.js.map +1 -1
- package/package.json +9 -8
package/CHANGELOG.md
CHANGED
|
@@ -11,7 +11,36 @@ change in a minor release.
|
|
|
11
11
|
|
|
12
12
|
## [Unreleased]
|
|
13
13
|
|
|
14
|
-
## [1.
|
|
14
|
+
## [1.5.0-beta.0]
|
|
15
|
+
|
|
16
|
+
### Added
|
|
17
|
+
|
|
18
|
+
- `/pi-desktop` (`/desktop` on other platforms) sends a private 24-hour link to the conversation's
|
|
19
|
+
desktop in the managed `oci` sandbox: watch or use the browser the agent drives, open its tabs,
|
|
20
|
+
DevTools, or a terminal. "Take control" pauses the agent's browser until you hand it back, and the
|
|
21
|
+
agent can hand you a step such as signing in with `jev_browser`'s `handOff`, which posts the step
|
|
22
|
+
with a link and ends its turn. The desktop starts on the first link, stops with the sandbox's idle
|
|
23
|
+
stop, and keeps the sandbox from stopping while someone has it open. It needs the sandbox image of
|
|
24
|
+
this release; an office whose sandbox still runs an older image gets it after the sandbox idles out.
|
|
25
|
+
Register `/pi-desktop` in the Slack app manifest, or let mikan do it (next entry).
|
|
26
|
+
- mikan creates and updates its Slack app (ADR 0026). With a Slack app configuration token,
|
|
27
|
+
`mikan onboard` creates the app from mikan's manifest, and every start replaces the app's
|
|
28
|
+
configuration with the manifest of the running release, so new slash commands, scopes, and events
|
|
29
|
+
need no edit in Slack's settings. Store a token for an existing app with `mikan slack token`, and
|
|
30
|
+
set the app's and bot's name and the description with `mikan slack set`. Settings added by hand in Slack are
|
|
31
|
+
removed. When a release adds a scope, mikan DMs the token's owner a reinstall link. Without a
|
|
32
|
+
token nothing changes.
|
|
33
|
+
|
|
34
|
+
### Changed
|
|
35
|
+
|
|
36
|
+
- `jev_browser` closes only tabs it opened when it passes its three-tab limit, so tabs a person
|
|
37
|
+
opened stay open.
|
|
38
|
+
- The sandbox image adds `sway`, `wayvnc`, and `foot` (about 1 MB) and the `mikan-desktop` script.
|
|
39
|
+
- Upgraded Pi to 1.2.0. Pi 1.2 removed an export mikan 1.4.0 imports, so a fresh install of 1.4.0
|
|
40
|
+
now fails to start; this release works with it. A nonzero `bash` exit is now an error result that
|
|
41
|
+
carries the output and the exit code, instead of a thrown error.
|
|
42
|
+
|
|
43
|
+
## [1.4.0] - 2026-10-11
|
|
15
44
|
|
|
16
45
|
### Added
|
|
17
46
|
|
|
@@ -26,8 +55,9 @@ change in a minor release.
|
|
|
26
55
|
- `--sandbox=oci` without an image uses `ghcr.io/geminixiang/mikan-sandbox:<mikan version>`, so
|
|
27
56
|
the sandbox image upgrades and rolls back with mikan. At startup mikan pulls the configured
|
|
28
57
|
image in the background; a failed pull logs a warning and offices keep the local image. A
|
|
29
|
-
stopped office's container is replaced when its image changed
|
|
30
|
-
once no container uses it
|
|
58
|
+
stopped office's container is replaced when its image changed. After any replacement, the
|
|
59
|
+
previous image is removed once no container uses it; an image that still has another name is
|
|
60
|
+
kept. A running container is never replaced.
|
|
31
61
|
|
|
32
62
|
### Changed
|
|
33
63
|
|
|
@@ -39,6 +69,9 @@ change in a minor release.
|
|
|
39
69
|
40–180 ms, and commands end as soon as mikan disconnects. String commands run with `bash` when
|
|
40
70
|
the container has it, and oversized command output is saved in the container's temporary
|
|
41
71
|
directory.
|
|
72
|
+
- Each office with a running container keeps one `<engine> exec -i` client process on the host,
|
|
73
|
+
the connection to its `pi-env` daemon. Commands left by a version before this one are no longer
|
|
74
|
+
swept at startup; they end when their container stops or is replaced.
|
|
42
75
|
- Each container gets the mikan host's time zone. The sandbox image no longer sets
|
|
43
76
|
`TZ=Asia/Taipei` and is about 390 MB smaller: `vim`, `nano`, `less`, `bash-completion`,
|
|
44
77
|
`python3-venv`, `gnupg`, and gcloud's precompiled Python files are gone.
|
|
@@ -48,6 +81,13 @@ change in a minor release.
|
|
|
48
81
|
one engine, mikan may pick a different engine and create new containers there; set
|
|
49
82
|
`CONTAINER_ENGINE=docker` to keep Docker.
|
|
50
83
|
|
|
84
|
+
### Security
|
|
85
|
+
|
|
86
|
+
- With a vault resolver, a conversation has no execution environment until its first run resolves
|
|
87
|
+
one, so a tool call that arrives earlier fails instead of running on the host.
|
|
88
|
+
- Container credentials reach the sandbox through the `pi-env` connection instead of command
|
|
89
|
+
arguments or a host-side env file.
|
|
90
|
+
|
|
51
91
|
## [1.3.0] - 2026-10-10
|
|
52
92
|
|
|
53
93
|
### Added
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"desktop.d.ts","sourceRoot":"","sources":["../../../src/adapters/commands/desktop.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,cAAc,EAAE,cAAc,EAAE,MAAM,YAAY,CAAC;AAMjE,qBAAa,qBAAsB,YAAW,cAAc;IACpD,SAAS,CAAC,OAAO,EAAE,cAAc,GAAG,OAAO,CAAC,OAAO,CAAC,CAuDzD;CACF"}
|
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
import { commandForms, matchCommand } from "./manifest.js";
|
|
2
|
+
import { portalNotConfiguredLines, replySummaryPrivately } from "./utils.js";
|
|
3
|
+
const DESKTOP_COMMANDS = commandForms("desktop");
|
|
4
|
+
const TITLE = "Desktop";
|
|
5
|
+
export class DesktopCommandHandler {
|
|
6
|
+
async tryHandle(context) {
|
|
7
|
+
if (!matchCommand(context.commandText, DESKTOP_COMMANDS))
|
|
8
|
+
return false;
|
|
9
|
+
if (!context.privateConversation &&
|
|
10
|
+
!context.bot.postPrivate &&
|
|
11
|
+
!context.bot.postPrivateDiagnostic) {
|
|
12
|
+
await replySummaryPrivately(context, TITLE, [
|
|
13
|
+
"A desktop link gives full use of this conversation's sandbox, so it is sent only privately. Ask for it in a direct message with the bot.",
|
|
14
|
+
]);
|
|
15
|
+
return true;
|
|
16
|
+
}
|
|
17
|
+
const { sandbox, openDesktop, desktopTokenStore, portalBaseUrl } = context.services;
|
|
18
|
+
if (sandbox.type !== "oci" || !openDesktop) {
|
|
19
|
+
await replySummaryPrivately(context, TITLE, [
|
|
20
|
+
"The desktop runs in a sandbox that mikan manages; start mikan with --sandbox=oci to use it.",
|
|
21
|
+
]);
|
|
22
|
+
return true;
|
|
23
|
+
}
|
|
24
|
+
if (!portalBaseUrl || !desktopTokenStore) {
|
|
25
|
+
await replySummaryPrivately(context, TITLE, portalNotConfiguredLines("Desktop"));
|
|
26
|
+
return true;
|
|
27
|
+
}
|
|
28
|
+
const started = await openDesktop({
|
|
29
|
+
address: context.address,
|
|
30
|
+
userId: context.platformUserId,
|
|
31
|
+
trustModel: context.messagingInfo.trustModel,
|
|
32
|
+
});
|
|
33
|
+
if (started === "outdated-image") {
|
|
34
|
+
await replySummaryPrivately(context, TITLE, [
|
|
35
|
+
"This conversation's sandbox is still running an older sandbox image without the desktop.",
|
|
36
|
+
"It moves to the current image once it has been idle and stops; ask again after that.",
|
|
37
|
+
]);
|
|
38
|
+
return true;
|
|
39
|
+
}
|
|
40
|
+
if (started === "stopped") {
|
|
41
|
+
await replySummaryPrivately(context, TITLE, [
|
|
42
|
+
"This conversation's sandbox could not start. Try again, or ask the operator to check it.",
|
|
43
|
+
]);
|
|
44
|
+
return true;
|
|
45
|
+
}
|
|
46
|
+
const { token } = desktopTokenStore.create({
|
|
47
|
+
platform: context.platform,
|
|
48
|
+
platformUserId: context.platformUserId,
|
|
49
|
+
office: context.services.workspace.office(context.address),
|
|
50
|
+
});
|
|
51
|
+
await replySummaryPrivately(context, TITLE, [
|
|
52
|
+
`${portalBaseUrl}/desktop?token=${token}`,
|
|
53
|
+
"Expires: 24 hours",
|
|
54
|
+
]);
|
|
55
|
+
return true;
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
//# sourceMappingURL=desktop.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"desktop.js","sourceRoot":"","sources":["../../../src/adapters/commands/desktop.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,YAAY,EAAE,YAAY,EAAE,MAAM,eAAe,CAAC;AAE3D,OAAO,EAAE,wBAAwB,EAAE,qBAAqB,EAAE,MAAM,YAAY,CAAC;AAE7E,MAAM,gBAAgB,GAAG,YAAY,CAAC,SAAS,CAAC,CAAC;AACjD,MAAM,KAAK,GAAG,SAAS,CAAC;AAExB,MAAM,OAAO,qBAAqB;IAChC,KAAK,CAAC,SAAS,CAAC,OAAuB;QACrC,IAAI,CAAC,YAAY,CAAC,OAAO,CAAC,WAAW,EAAE,gBAAgB,CAAC;YAAE,OAAO,KAAK,CAAC;QAEvE,IACE,CAAC,OAAO,CAAC,mBAAmB;YAC5B,CAAC,OAAO,CAAC,GAAG,CAAC,WAAW;YACxB,CAAC,OAAO,CAAC,GAAG,CAAC,qBAAqB,EAClC,CAAC;YACD,MAAM,qBAAqB,CAAC,OAAO,EAAE,KAAK,EAAE;gBAC1C,0IAA0I;aAC3I,CAAC,CAAC;YACH,OAAO,IAAI,CAAC;QACd,CAAC;QAED,MAAM,EAAE,OAAO,EAAE,WAAW,EAAE,iBAAiB,EAAE,aAAa,EAAE,GAAG,OAAO,CAAC,QAAQ,CAAC;QACpF,IAAI,OAAO,CAAC,IAAI,KAAK,KAAK,IAAI,CAAC,WAAW,EAAE,CAAC;YAC3C,MAAM,qBAAqB,CAAC,OAAO,EAAE,KAAK,EAAE;gBAC1C,6FAA6F;aAC9F,CAAC,CAAC;YACH,OAAO,IAAI,CAAC;QACd,CAAC;QACD,IAAI,CAAC,aAAa,IAAI,CAAC,iBAAiB,EAAE,CAAC;YACzC,MAAM,qBAAqB,CAAC,OAAO,EAAE,KAAK,EAAE,wBAAwB,CAAC,SAAS,CAAC,CAAC,CAAC;YACjF,OAAO,IAAI,CAAC;QACd,CAAC;QAED,MAAM,OAAO,GAAG,MAAM,WAAW,CAAC;YAChC,OAAO,EAAE,OAAO,CAAC,OAAO;YACxB,MAAM,EAAE,OAAO,CAAC,cAAc;YAC9B,UAAU,EAAE,OAAO,CAAC,aAAa,CAAC,UAAU;SAC7C,CAAC,CAAC;QACH,IAAI,OAAO,KAAK,gBAAgB,EAAE,CAAC;YACjC,MAAM,qBAAqB,CAAC,OAAO,EAAE,KAAK,EAAE;gBAC1C,0FAA0F;gBAC1F,sFAAsF;aACvF,CAAC,CAAC;YACH,OAAO,IAAI,CAAC;QACd,CAAC;QACD,IAAI,OAAO,KAAK,SAAS,EAAE,CAAC;YAC1B,MAAM,qBAAqB,CAAC,OAAO,EAAE,KAAK,EAAE;gBAC1C,0FAA0F;aAC3F,CAAC,CAAC;YACH,OAAO,IAAI,CAAC;QACd,CAAC;QAED,MAAM,EAAE,KAAK,EAAE,GAAG,iBAAiB,CAAC,MAAM,CAAC;YACzC,QAAQ,EAAE,OAAO,CAAC,QAAQ;YAC1B,cAAc,EAAE,OAAO,CAAC,cAAc;YACtC,MAAM,EAAE,OAAO,CAAC,QAAQ,CAAC,SAAS,CAAC,MAAM,CAAC,OAAO,CAAC,OAAO,CAAC;SAC3D,CAAC,CAAC;QACH,MAAM,qBAAqB,CAAC,OAAO,EAAE,KAAK,EAAE;YAC1C,GAAG,aAAa,kBAAkB,KAAK,EAAE;YACzC,mBAAmB;SACpB,CAAC,CAAC;QACH,OAAO,IAAI,CAAC;IACd,CAAC;CACF","sourcesContent":["import { commandForms, matchCommand } from \"./manifest.js\";\nimport type { CommandContext, CommandHandler } from \"./types.js\";\nimport { portalNotConfiguredLines, replySummaryPrivately } from \"./utils.js\";\n\nconst DESKTOP_COMMANDS = commandForms(\"desktop\");\nconst TITLE = \"Desktop\";\n\nexport class DesktopCommandHandler implements CommandHandler {\n async tryHandle(context: CommandContext): Promise<boolean> {\n if (!matchCommand(context.commandText, DESKTOP_COMMANDS)) return false;\n\n if (\n !context.privateConversation &&\n !context.bot.postPrivate &&\n !context.bot.postPrivateDiagnostic\n ) {\n await replySummaryPrivately(context, TITLE, [\n \"A desktop link gives full use of this conversation's sandbox, so it is sent only privately. Ask for it in a direct message with the bot.\",\n ]);\n return true;\n }\n\n const { sandbox, openDesktop, desktopTokenStore, portalBaseUrl } = context.services;\n if (sandbox.type !== \"oci\" || !openDesktop) {\n await replySummaryPrivately(context, TITLE, [\n \"The desktop runs in a sandbox that mikan manages; start mikan with --sandbox=oci to use it.\",\n ]);\n return true;\n }\n if (!portalBaseUrl || !desktopTokenStore) {\n await replySummaryPrivately(context, TITLE, portalNotConfiguredLines(\"Desktop\"));\n return true;\n }\n\n const started = await openDesktop({\n address: context.address,\n userId: context.platformUserId,\n trustModel: context.messagingInfo.trustModel,\n });\n if (started === \"outdated-image\") {\n await replySummaryPrivately(context, TITLE, [\n \"This conversation's sandbox is still running an older sandbox image without the desktop.\",\n \"It moves to the current image once it has been idle and stops; ask again after that.\",\n ]);\n return true;\n }\n if (started === \"stopped\") {\n await replySummaryPrivately(context, TITLE, [\n \"This conversation's sandbox could not start. Try again, or ask the operator to check it.\",\n ]);\n return true;\n }\n\n const { token } = desktopTokenStore.create({\n platform: context.platform,\n platformUserId: context.platformUserId,\n office: context.services.workspace.office(context.address),\n });\n await replySummaryPrivately(context, TITLE, [\n `${portalBaseUrl}/desktop?token=${token}`,\n \"Expires: 24 hours\",\n ]);\n return true;\n }\n}\n"]}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"manifest.d.ts","sourceRoot":"","sources":["../../../src/adapters/commands/manifest.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,oBAAoB,EAAE,MAAM,YAAY,CAAC;AAEvD,eAAO,MAAM,gBAAgB,EAAE,SAAS,oBAAoB,
|
|
1
|
+
{"version":3,"file":"manifest.d.ts","sourceRoot":"","sources":["../../../src/adapters/commands/manifest.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,oBAAoB,EAAE,MAAM,YAAY,CAAC;AAEvD,eAAO,MAAM,gBAAgB,EAAE,SAAS,oBAAoB,EAwG3D,CAAC;AAEF,wBAAgB,oBAAoB,CAAC,IAAI,EAAE,MAAM,GAAG,oBAAoB,CAIvE;AAED,wBAAgB,UAAU,CAAC,IAAI,EAAE,MAAM,GAAG,SAAS,MAAM,EAAE,CAM1D;AAED,wBAAgB,YAAY,CAAC,IAAI,EAAE,MAAM,GAAG,SAAS,MAAM,EAAE,CAG5D;AAED,wBAAgB,mBAAmB,IAAI;IAAE,OAAO,EAAE,MAAM,CAAC;IAAC,WAAW,EAAE,MAAM,CAAA;CAAE,EAAE,CAKhF;AAED,wBAAgB,YAAY,CAAC,OAAO,SAAS,MAAM,EACjD,IAAI,EAAE,MAAM,EACZ,OAAO,EAAE,SAAS,OAAO,EAAE,EAC3B,OAAO,CAAC,EAAE;IAAE,YAAY,CAAC,EAAE,OAAO,CAAA;CAAE,GACnC;IAAE,OAAO,EAAE,OAAO,CAAC;IAAC,IAAI,EAAE,MAAM,EAAE,CAAA;CAAE,GAAG,IAAI,CAQ7C;AASD,wBAAgB,aAAa,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAEnD"}
|
|
@@ -16,6 +16,14 @@ export const COMMAND_MANIFEST = [
|
|
|
16
16
|
discord: true,
|
|
17
17
|
telegramMenu: {},
|
|
18
18
|
},
|
|
19
|
+
{
|
|
20
|
+
name: "desktop",
|
|
21
|
+
description: "Open this conversation's desktop to watch or use its browser",
|
|
22
|
+
slackCommand: "/pi-desktop",
|
|
23
|
+
slackRoute: {},
|
|
24
|
+
discord: true,
|
|
25
|
+
telegramMenu: {},
|
|
26
|
+
},
|
|
19
27
|
{
|
|
20
28
|
name: "model",
|
|
21
29
|
description: "Switch this conversation's LLM model",
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"manifest.js","sourceRoot":"","sources":["../../../src/adapters/commands/manifest.ts"],"names":[],"mappings":"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","sourcesContent":["import type { CommandManifestEntry } from \"./types.js\";\n\nexport const COMMAND_MANIFEST: readonly CommandManifestEntry[] = [\n {\n name: \"login\",\n description: \"Store credentials in your private vault\",\n slackCommand: \"/pi-login\",\n slackRoute: { includeText: true, privateCommand: true },\n discord: true,\n telegramMenu: {},\n },\n {\n name: \"session\",\n description: \"Open the current session in the web viewer\",\n bare: true,\n slackCommand: \"/pi-session\",\n slackRoute: { thread: true },\n discord: true,\n telegramMenu: {},\n },\n {\n name: \"model\",\n description: \"Switch this conversation's LLM model\",\n arg: {\n name: \"model\",\n description: \"provider/model[:thinking], e.g. anthropic/claude-sonnet-4-6:off\",\n required: false,\n },\n slackCommand: \"/pi-model\",\n slackRoute: { includeText: true },\n discord: true,\n telegramMenu: {},\n },\n {\n name: \"sandbox\",\n description: \"Show sandbox status or boost limits\",\n arg: {\n name: \"action\",\n description: \"'boost' temporarily applies the configured boost limits\",\n required: false,\n },\n slackCommand: \"/pi-sandbox\",\n slackRoute: { includeText: true },\n discord: true,\n telegramMenu: { description: \"Show or boost sandbox limits\" },\n telegramCommand: true,\n },\n {\n name: \"autoreply\",\n aliases: [\"auto-reply\"],\n description: \"Enable, disable, or Jev-assist replies without mentions\",\n arg: {\n name: \"state\",\n description: \"on, off, or jev\",\n required: true,\n },\n slackCommand: \"/pi-auto-reply\",\n slackRoute: { includeText: true },\n },\n {\n name: \"stop\",\n description: \"Stop the current conversation\",\n magicWord: true,\n discord: true,\n telegramMenu: { description: \"Stop ongoing conversation\" },\n },\n {\n name: \"new\",\n description: \"Reset conversation history and start fresh\",\n slackCommand: \"/pi-new\",\n slackRoute: {},\n discord: true,\n telegramMenu: {},\n telegramCommand: true,\n },\n {\n name: \"compact\",\n description: \"Summarize older messages to free up context\",\n arg: {\n name: \"instructions\",\n description: \"What the summary should keep\",\n required: false,\n },\n slackCommand: \"/pi-compact\",\n slackRoute: { includeText: true },\n discord: true,\n telegramMenu: {},\n telegramCommand: true,\n },\n {\n name: \"admin\",\n description: \"Open the admin portal\",\n slackCommand: \"/pi-admin\",\n slackRoute: { thread: true },\n discord: true,\n telegramMenu: {},\n telegramCommand: true,\n },\n];\n\nexport function commandManifestEntry(name: string): CommandManifestEntry {\n const entry = COMMAND_MANIFEST.find((candidate) => candidate.name === name);\n if (!entry) throw new Error(`Unknown command in manifest: ${name}`);\n return entry;\n}\n\nexport function slashForms(name: string): readonly string[] {\n const entry = commandManifestEntry(name);\n return [entry.name, ...(entry.aliases ?? [])].flatMap((spelling) => [\n `/${spelling}`,\n `/pi-${spelling}`,\n ]);\n}\n\nexport function commandForms(name: string): readonly string[] {\n const entry = commandManifestEntry(name);\n return [...(entry.bare ? [entry.name] : []), ...slashForms(name)];\n}\n\nexport function telegramCommandMenu(): { command: string; description: string }[] {\n return COMMAND_MANIFEST.filter((entry) => entry.telegramMenu).map((entry) => ({\n command: entry.name,\n description: entry.telegramMenu?.description ?? entry.description,\n }));\n}\n\nexport function matchCommand<Command extends string>(\n text: string,\n aliases: readonly Command[],\n options?: { stripMention?: boolean },\n): { command: Command; args: string[] } | null {\n const tokens = text.trim().split(/\\s+/).filter(Boolean);\n const first = tokens[0];\n if (first === undefined) return null;\n\n const token = options?.stripMention ? first.replace(/@\\w+$/i, \"\") : first;\n const command = token.toLowerCase() as Command;\n return aliases.includes(command) ? { command, args: tokens.slice(1) } : null;\n}\n\nconst COMMAND_NAMES = COMMAND_MANIFEST.flatMap((entry) => [entry.name].concat(entry.aliases ?? []));\n\nconst COMMAND_TEXT_PATTERN = new RegExp(\n `^\\\\/(?:pi-[\\\\w-]+|${COMMAND_NAMES.join(\"|\")})(?:@\\\\w+)?(?:\\\\s|$)`,\n \"i\",\n);\n\nexport function isCommandText(text: string): boolean {\n return COMMAND_TEXT_PATTERN.test(text.trim());\n}\n"]}
|
|
1
|
+
{"version":3,"file":"manifest.js","sourceRoot":"","sources":["../../../src/adapters/commands/manifest.ts"],"names":[],"mappings":"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","sourcesContent":["import type { CommandManifestEntry } from \"./types.js\";\n\nexport const COMMAND_MANIFEST: readonly CommandManifestEntry[] = [\n {\n name: \"login\",\n description: \"Store credentials in your private vault\",\n slackCommand: \"/pi-login\",\n slackRoute: { includeText: true, privateCommand: true },\n discord: true,\n telegramMenu: {},\n },\n {\n name: \"session\",\n description: \"Open the current session in the web viewer\",\n bare: true,\n slackCommand: \"/pi-session\",\n slackRoute: { thread: true },\n discord: true,\n telegramMenu: {},\n },\n {\n name: \"desktop\",\n description: \"Open this conversation's desktop to watch or use its browser\",\n slackCommand: \"/pi-desktop\",\n slackRoute: {},\n discord: true,\n telegramMenu: {},\n },\n {\n name: \"model\",\n description: \"Switch this conversation's LLM model\",\n arg: {\n name: \"model\",\n description: \"provider/model[:thinking], e.g. anthropic/claude-sonnet-4-6:off\",\n required: false,\n },\n slackCommand: \"/pi-model\",\n slackRoute: { includeText: true },\n discord: true,\n telegramMenu: {},\n },\n {\n name: \"sandbox\",\n description: \"Show sandbox status or boost limits\",\n arg: {\n name: \"action\",\n description: \"'boost' temporarily applies the configured boost limits\",\n required: false,\n },\n slackCommand: \"/pi-sandbox\",\n slackRoute: { includeText: true },\n discord: true,\n telegramMenu: { description: \"Show or boost sandbox limits\" },\n telegramCommand: true,\n },\n {\n name: \"autoreply\",\n aliases: [\"auto-reply\"],\n description: \"Enable, disable, or Jev-assist replies without mentions\",\n arg: {\n name: \"state\",\n description: \"on, off, or jev\",\n required: true,\n },\n slackCommand: \"/pi-auto-reply\",\n slackRoute: { includeText: true },\n },\n {\n name: \"stop\",\n description: \"Stop the current conversation\",\n magicWord: true,\n discord: true,\n telegramMenu: { description: \"Stop ongoing conversation\" },\n },\n {\n name: \"new\",\n description: \"Reset conversation history and start fresh\",\n slackCommand: \"/pi-new\",\n slackRoute: {},\n discord: true,\n telegramMenu: {},\n telegramCommand: true,\n },\n {\n name: \"compact\",\n description: \"Summarize older messages to free up context\",\n arg: {\n name: \"instructions\",\n description: \"What the summary should keep\",\n required: false,\n },\n slackCommand: \"/pi-compact\",\n slackRoute: { includeText: true },\n discord: true,\n telegramMenu: {},\n telegramCommand: true,\n },\n {\n name: \"admin\",\n description: \"Open the admin portal\",\n slackCommand: \"/pi-admin\",\n slackRoute: { thread: true },\n discord: true,\n telegramMenu: {},\n telegramCommand: true,\n },\n];\n\nexport function commandManifestEntry(name: string): CommandManifestEntry {\n const entry = COMMAND_MANIFEST.find((candidate) => candidate.name === name);\n if (!entry) throw new Error(`Unknown command in manifest: ${name}`);\n return entry;\n}\n\nexport function slashForms(name: string): readonly string[] {\n const entry = commandManifestEntry(name);\n return [entry.name, ...(entry.aliases ?? [])].flatMap((spelling) => [\n `/${spelling}`,\n `/pi-${spelling}`,\n ]);\n}\n\nexport function commandForms(name: string): readonly string[] {\n const entry = commandManifestEntry(name);\n return [...(entry.bare ? [entry.name] : []), ...slashForms(name)];\n}\n\nexport function telegramCommandMenu(): { command: string; description: string }[] {\n return COMMAND_MANIFEST.filter((entry) => entry.telegramMenu).map((entry) => ({\n command: entry.name,\n description: entry.telegramMenu?.description ?? entry.description,\n }));\n}\n\nexport function matchCommand<Command extends string>(\n text: string,\n aliases: readonly Command[],\n options?: { stripMention?: boolean },\n): { command: Command; args: string[] } | null {\n const tokens = text.trim().split(/\\s+/).filter(Boolean);\n const first = tokens[0];\n if (first === undefined) return null;\n\n const token = options?.stripMention ? first.replace(/@\\w+$/i, \"\") : first;\n const command = token.toLowerCase() as Command;\n return aliases.includes(command) ? { command, args: tokens.slice(1) } : null;\n}\n\nconst COMMAND_NAMES = COMMAND_MANIFEST.flatMap((entry) => [entry.name].concat(entry.aliases ?? []));\n\nconst COMMAND_TEXT_PATTERN = new RegExp(\n `^\\\\/(?:pi-[\\\\w-]+|${COMMAND_NAMES.join(\"|\")})(?:@\\\\w+)?(?:\\\\s|$)`,\n \"i\",\n);\n\nexport function isCommandText(text: string): boolean {\n return COMMAND_TEXT_PATTERN.test(text.trim());\n}\n"]}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"registry.d.ts","sourceRoot":"","sources":["../../../src/adapters/commands/registry.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"registry.d.ts","sourceRoot":"","sources":["../../../src/adapters/commands/registry.ts"],"names":[],"mappings":"AAUA,OAAO,KAAK,EAAE,cAAc,EAAE,cAAc,EAAE,aAAa,EAAE,MAAM,YAAY,CAAC;AAchF,wBAAgB,sBAAsB,CAAC,aAAa,EAAE,aAAa,GAAG,cAAc,EAAE,CAUrF;AAED,wBAAsB,eAAe,CACnC,QAAQ,EAAE,SAAS,cAAc,EAAE,EACnC,OAAO,EAAE,cAAc,GACtB,OAAO,CAAC,OAAO,CAAC,CAKlB"}
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { AdminCommandHandler } from "./admin.js";
|
|
2
2
|
import { AutoReplyCommandHandler } from "./auto-reply.js";
|
|
3
3
|
import { CompactCommandHandler } from "./compact.js";
|
|
4
|
+
import { DesktopCommandHandler } from "./desktop.js";
|
|
4
5
|
import { LoginCommandHandler } from "./login.js";
|
|
5
6
|
import { COMMAND_MANIFEST } from "./manifest.js";
|
|
6
7
|
import { ModelCommandHandler } from "./model.js";
|
|
@@ -12,6 +13,7 @@ const HANDLER_FACTORIES = {
|
|
|
12
13
|
autoreply: () => new AutoReplyCommandHandler(),
|
|
13
14
|
login: () => new LoginCommandHandler(),
|
|
14
15
|
session: () => new SessionViewCommandHandler(),
|
|
16
|
+
desktop: () => new DesktopCommandHandler(),
|
|
15
17
|
model: (modelRegistry) => new ModelCommandHandler(modelRegistry),
|
|
16
18
|
sandbox: () => new SandboxCommandHandler(),
|
|
17
19
|
new: () => new NewCommandHandler(),
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"registry.js","sourceRoot":"","sources":["../../../src/adapters/commands/registry.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,mBAAmB,EAAE,MAAM,YAAY,CAAC;AACjD,OAAO,EAAE,uBAAuB,EAAE,MAAM,iBAAiB,CAAC;AAC1D,OAAO,EAAE,qBAAqB,EAAE,MAAM,cAAc,CAAC;AACrD,OAAO,EAAE,mBAAmB,EAAE,MAAM,YAAY,CAAC;AACjD,OAAO,EAAE,gBAAgB,EAAE,MAAM,eAAe,CAAC;AACjD,OAAO,EAAE,mBAAmB,EAAE,MAAM,YAAY,CAAC;AACjD,OAAO,EAAE,iBAAiB,EAAE,MAAM,UAAU,CAAC;AAC7C,OAAO,EAAE,qBAAqB,EAAE,MAAM,cAAc,CAAC;AACrD,OAAO,EAAE,yBAAyB,EAAE,MAAM,mBAAmB,CAAC;AAG9D,MAAM,iBAAiB,GAAqE;IAC1F,KAAK,EAAE,GAAG,EAAE,CAAC,IAAI,mBAAmB,EAAE;IACtC,SAAS,EAAE,GAAG,EAAE,CAAC,IAAI,uBAAuB,EAAE;IAC9C,KAAK,EAAE,GAAG,EAAE,CAAC,IAAI,mBAAmB,EAAE;IACtC,OAAO,EAAE,GAAG,EAAE,CAAC,IAAI,yBAAyB,EAAE;IAC9C,KAAK,EAAE,CAAC,aAAa,EAAE,EAAE,CAAC,IAAI,mBAAmB,CAAC,aAAa,CAAC;IAChE,OAAO,EAAE,GAAG,EAAE,CAAC,IAAI,qBAAqB,EAAE;IAC1C,GAAG,EAAE,GAAG,EAAE,CAAC,IAAI,iBAAiB,EAAE;IAClC,OAAO,EAAE,GAAG,EAAE,CAAC,IAAI,qBAAqB,EAAE;CAC3C,CAAC;AAEF,MAAM,UAAU,sBAAsB,CAAC,aAA4B;IACjE,OAAO,gBAAgB,CAAC,MAAM,CAAC,CAAC,KAAK,EAAE,EAAE,CAAC,CAAC,KAAK,CAAC,SAAS,CAAC,CAAC,GAAG,CAAC,CAAC,KAAK,EAAE,EAAE;QACxE,MAAM,OAAO,GAAG,iBAAiB,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC;QAC9C,IAAI,CAAC,OAAO,EAAE,CAAC;YACb,MAAM,IAAI,KAAK,CACb,2BAA2B,KAAK,CAAC,IAAI,kDAAkD,CACxF,CAAC;QACJ,CAAC;QACD,OAAO,OAAO,CAAC,aAAa,CAAC,CAAC;IAChC,CAAC,CAAC,CAAC;AACL,CAAC;AAED,MAAM,CAAC,KAAK,UAAU,eAAe,CACnC,QAAmC,EACnC,OAAuB;IAEvB,KAAK,MAAM,OAAO,IAAI,QAAQ,EAAE,CAAC;QAC/B,IAAI,MAAM,OAAO,CAAC,SAAS,CAAC,OAAO,CAAC;YAAE,OAAO,IAAI,CAAC;IACpD,CAAC;IACD,OAAO,KAAK,CAAC;AACf,CAAC","sourcesContent":["import { AdminCommandHandler } from \"./admin.js\";\nimport { AutoReplyCommandHandler } from \"./auto-reply.js\";\nimport { CompactCommandHandler } from \"./compact.js\";\nimport { LoginCommandHandler } from \"./login.js\";\nimport { COMMAND_MANIFEST } from \"./manifest.js\";\nimport { ModelCommandHandler } from \"./model.js\";\nimport { NewCommandHandler } from \"./new.js\";\nimport { SandboxCommandHandler } from \"./sandbox.js\";\nimport { SessionViewCommandHandler } from \"./session-view.js\";\nimport type { CommandContext, CommandHandler, ModelRegistry } from \"./types.js\";\n\nconst HANDLER_FACTORIES: Record<string, (modelRegistry: ModelRegistry) => CommandHandler> = {\n admin: () => new AdminCommandHandler(),\n autoreply: () => new AutoReplyCommandHandler(),\n login: () => new LoginCommandHandler(),\n session: () => new SessionViewCommandHandler(),\n model: (modelRegistry) => new ModelCommandHandler(modelRegistry),\n sandbox: () => new SandboxCommandHandler(),\n new: () => new NewCommandHandler(),\n compact: () => new CompactCommandHandler(),\n};\n\nexport function defaultCommandHandlers(modelRegistry: ModelRegistry): CommandHandler[] {\n return COMMAND_MANIFEST.filter((entry) => !entry.magicWord).map((entry) => {\n const factory = HANDLER_FACTORIES[entry.name];\n if (!factory) {\n throw new Error(\n `Command manifest entry \"${entry.name}\" has no handler factory in commands/registry.ts`,\n );\n }\n return factory(modelRegistry);\n });\n}\n\nexport async function dispatchCommand(\n handlers: readonly CommandHandler[],\n context: CommandContext,\n): Promise<boolean> {\n for (const handler of handlers) {\n if (await handler.tryHandle(context)) return true;\n }\n return false;\n}\n"]}
|
|
1
|
+
{"version":3,"file":"registry.js","sourceRoot":"","sources":["../../../src/adapters/commands/registry.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,mBAAmB,EAAE,MAAM,YAAY,CAAC;AACjD,OAAO,EAAE,uBAAuB,EAAE,MAAM,iBAAiB,CAAC;AAC1D,OAAO,EAAE,qBAAqB,EAAE,MAAM,cAAc,CAAC;AACrD,OAAO,EAAE,qBAAqB,EAAE,MAAM,cAAc,CAAC;AACrD,OAAO,EAAE,mBAAmB,EAAE,MAAM,YAAY,CAAC;AACjD,OAAO,EAAE,gBAAgB,EAAE,MAAM,eAAe,CAAC;AACjD,OAAO,EAAE,mBAAmB,EAAE,MAAM,YAAY,CAAC;AACjD,OAAO,EAAE,iBAAiB,EAAE,MAAM,UAAU,CAAC;AAC7C,OAAO,EAAE,qBAAqB,EAAE,MAAM,cAAc,CAAC;AACrD,OAAO,EAAE,yBAAyB,EAAE,MAAM,mBAAmB,CAAC;AAG9D,MAAM,iBAAiB,GAAqE;IAC1F,KAAK,EAAE,GAAG,EAAE,CAAC,IAAI,mBAAmB,EAAE;IACtC,SAAS,EAAE,GAAG,EAAE,CAAC,IAAI,uBAAuB,EAAE;IAC9C,KAAK,EAAE,GAAG,EAAE,CAAC,IAAI,mBAAmB,EAAE;IACtC,OAAO,EAAE,GAAG,EAAE,CAAC,IAAI,yBAAyB,EAAE;IAC9C,OAAO,EAAE,GAAG,EAAE,CAAC,IAAI,qBAAqB,EAAE;IAC1C,KAAK,EAAE,CAAC,aAAa,EAAE,EAAE,CAAC,IAAI,mBAAmB,CAAC,aAAa,CAAC;IAChE,OAAO,EAAE,GAAG,EAAE,CAAC,IAAI,qBAAqB,EAAE;IAC1C,GAAG,EAAE,GAAG,EAAE,CAAC,IAAI,iBAAiB,EAAE;IAClC,OAAO,EAAE,GAAG,EAAE,CAAC,IAAI,qBAAqB,EAAE;CAC3C,CAAC;AAEF,MAAM,UAAU,sBAAsB,CAAC,aAA4B;IACjE,OAAO,gBAAgB,CAAC,MAAM,CAAC,CAAC,KAAK,EAAE,EAAE,CAAC,CAAC,KAAK,CAAC,SAAS,CAAC,CAAC,GAAG,CAAC,CAAC,KAAK,EAAE,EAAE;QACxE,MAAM,OAAO,GAAG,iBAAiB,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC;QAC9C,IAAI,CAAC,OAAO,EAAE,CAAC;YACb,MAAM,IAAI,KAAK,CACb,2BAA2B,KAAK,CAAC,IAAI,kDAAkD,CACxF,CAAC;QACJ,CAAC;QACD,OAAO,OAAO,CAAC,aAAa,CAAC,CAAC;IAChC,CAAC,CAAC,CAAC;AACL,CAAC;AAED,MAAM,CAAC,KAAK,UAAU,eAAe,CACnC,QAAmC,EACnC,OAAuB;IAEvB,KAAK,MAAM,OAAO,IAAI,QAAQ,EAAE,CAAC;QAC/B,IAAI,MAAM,OAAO,CAAC,SAAS,CAAC,OAAO,CAAC;YAAE,OAAO,IAAI,CAAC;IACpD,CAAC;IACD,OAAO,KAAK,CAAC;AACf,CAAC","sourcesContent":["import { AdminCommandHandler } from \"./admin.js\";\nimport { AutoReplyCommandHandler } from \"./auto-reply.js\";\nimport { CompactCommandHandler } from \"./compact.js\";\nimport { DesktopCommandHandler } from \"./desktop.js\";\nimport { LoginCommandHandler } from \"./login.js\";\nimport { COMMAND_MANIFEST } from \"./manifest.js\";\nimport { ModelCommandHandler } from \"./model.js\";\nimport { NewCommandHandler } from \"./new.js\";\nimport { SandboxCommandHandler } from \"./sandbox.js\";\nimport { SessionViewCommandHandler } from \"./session-view.js\";\nimport type { CommandContext, CommandHandler, ModelRegistry } from \"./types.js\";\n\nconst HANDLER_FACTORIES: Record<string, (modelRegistry: ModelRegistry) => CommandHandler> = {\n admin: () => new AdminCommandHandler(),\n autoreply: () => new AutoReplyCommandHandler(),\n login: () => new LoginCommandHandler(),\n session: () => new SessionViewCommandHandler(),\n desktop: () => new DesktopCommandHandler(),\n model: (modelRegistry) => new ModelCommandHandler(modelRegistry),\n sandbox: () => new SandboxCommandHandler(),\n new: () => new NewCommandHandler(),\n compact: () => new CompactCommandHandler(),\n};\n\nexport function defaultCommandHandlers(modelRegistry: ModelRegistry): CommandHandler[] {\n return COMMAND_MANIFEST.filter((entry) => !entry.magicWord).map((entry) => {\n const factory = HANDLER_FACTORIES[entry.name];\n if (!factory) {\n throw new Error(\n `Command manifest entry \"${entry.name}\" has no handler factory in commands/registry.ts`,\n );\n }\n return factory(modelRegistry);\n });\n}\n\nexport async function dispatchCommand(\n handlers: readonly CommandHandler[],\n context: CommandContext,\n): Promise<boolean> {\n for (const handler of handlers) {\n if (await handler.tryHandle(context)) return true;\n }\n return false;\n}\n"]}
|
|
@@ -3,7 +3,7 @@ import type { Api, Model } from "@earendil-works/pi-ai";
|
|
|
3
3
|
import type { Workspace } from "../../office/types.js";
|
|
4
4
|
import type { DockerContainerManager } from "../../sandbox/provisioner.js";
|
|
5
5
|
import type { SandboxConfig } from "../../sandbox/types.js";
|
|
6
|
-
import type { SandboxResourceController, ConversationContext, CompactOutcome, HandleCompactCommandOptions, HandleNewCommandOptions, MessagingInfo, MessagingBot, OfficeAddress, PlatformName, RunnerCacheControl, SessionViewTokenStoreLike } from "../../types.js";
|
|
6
|
+
import type { SandboxResourceController, ConversationContext, CompactOutcome, HandleCompactCommandOptions, HandleNewCommandOptions, MessagingInfo, MessagingBot, OfficeAddress, PlatformName, RunnerCacheControl, SessionViewTokenStoreLike, DesktopTokenStoreLike, OpenDesktop } from "../../types.js";
|
|
7
7
|
import type { VaultManager } from "../../vault/types.js";
|
|
8
8
|
import type { AdminTokenCreateOptions } from "../web/admin/types.js";
|
|
9
9
|
interface CommandArgSpec {
|
|
@@ -54,6 +54,8 @@ export interface CommandServices {
|
|
|
54
54
|
resourceController?: SandboxResourceController;
|
|
55
55
|
linkTokenStore: LinkTokenStoreLike;
|
|
56
56
|
sessionViewTokenStore: SessionViewTokenStoreLike;
|
|
57
|
+
desktopTokenStore?: DesktopTokenStoreLike;
|
|
58
|
+
openDesktop?: OpenDesktop;
|
|
57
59
|
adminTokenStore: AdminTokenStoreLike;
|
|
58
60
|
portalBaseUrl?: string;
|
|
59
61
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../../src/adapters/commands/types.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,+BAA+B,CAAC;AACnE,OAAO,KAAK,EAAE,GAAG,EAAE,KAAK,EAAE,MAAM,uBAAuB,CAAC;AAExD,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,uBAAuB,CAAC;AACvD,OAAO,KAAK,EAAE,sBAAsB,EAAE,MAAM,8BAA8B,CAAC;AAC3E,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,wBAAwB,CAAC;AAC5D,OAAO,KAAK,EACV,yBAAyB,EACzB,mBAAmB,EACnB,cAAc,EACd,2BAA2B,EAC3B,uBAAuB,EACvB,aAAa,EACb,YAAY,EACZ,aAAa,EACb,YAAY,EACZ,kBAAkB,EAClB,yBAAyB,
|
|
1
|
+
{"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../../src/adapters/commands/types.ts"],"names":[],"mappings":"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"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"types.js","sourceRoot":"","sources":["../../../src/adapters/commands/types.ts"],"names":[],"mappings":"","sourcesContent":["import type { ThinkingLevel } from \"@earendil-works/pi-agent-core\";\nimport type { Api, Model } from \"@earendil-works/pi-ai\";\n\nimport type { Workspace } from \"../../office/types.js\";\nimport type { DockerContainerManager } from \"../../sandbox/provisioner.js\";\nimport type { SandboxConfig } from \"../../sandbox/types.js\";\nimport type {\n SandboxResourceController,\n ConversationContext,\n CompactOutcome,\n HandleCompactCommandOptions,\n HandleNewCommandOptions,\n MessagingInfo,\n MessagingBot,\n OfficeAddress,\n PlatformName,\n RunnerCacheControl,\n SessionViewTokenStoreLike,\n} from \"../../types.js\";\nimport type { VaultManager } from \"../../vault/types.js\";\nimport type { AdminTokenCreateOptions } from \"../web/admin/types.js\";\n\ninterface CommandArgSpec {\n name: string;\n description: string;\n required: boolean;\n}\n\nexport interface SlackSlashRoute {\n includeText?: boolean;\n thread?: boolean;\n privateCommand?: boolean;\n}\n\nexport interface CommandManifestEntry {\n name: string;\n description: string;\n aliases?: readonly string[];\n arg?: CommandArgSpec;\n bare?: boolean;\n magicWord?: boolean;\n slackCommand?: string;\n slackRoute?: SlackSlashRoute;\n discord?: boolean;\n telegramMenu?: { description?: string };\n telegramCommand?: boolean;\n}\n\nexport interface LinkTokenStoreLike {\n create(\n platform: PlatformName,\n platformUserId: string,\n conversationId: string,\n vaultId: string,\n providerId: string,\n ): { token: string };\n}\n\nexport interface AdminTokenStoreLike {\n create(options: AdminTokenCreateOptions): { token: string };\n}\n\ninterface CommandRuntimeBridge extends RunnerCacheControl {\n handleNewCommand(options: HandleNewCommandOptions): Promise<void>;\n handleCompactCommand(options: HandleCompactCommandOptions): Promise<CompactOutcome>;\n}\n\nexport interface CommandServices {\n workspace: Workspace;\n runtime?: CommandRuntimeBridge;\n sandbox: SandboxConfig;\n vaultManager: VaultManager;\n provisioner?: DockerContainerManager;\n resourceController?: SandboxResourceController;\n linkTokenStore: LinkTokenStoreLike;\n sessionViewTokenStore: SessionViewTokenStoreLike;\n adminTokenStore: AdminTokenStoreLike;\n portalBaseUrl?: string;\n}\n\nexport interface CommandContext {\n bot: MessagingBot;\n responder: ConversationContext[\"responder\"];\n platform: PlatformName;\n address: OfficeAddress;\n platformUserId: string;\n platformUserName?: string;\n conversationId: string;\n vaultConversationId?: string;\n sessionKey: string;\n commandText: string;\n privateConversation: boolean;\n messagingInfo: MessagingInfo;\n services: CommandServices;\n}\n\nexport interface CommandHandler {\n tryHandle(context: CommandContext): Promise<boolean>;\n}\n\nexport interface ModelRegistry {\n find(provider: string, modelId: string): Model<Api> | undefined;\n}\n\nexport interface ParsedModelCommand {\n provider?: string;\n model?: string;\n modelCandidate?: string;\n thinkingLevelCandidate?: string;\n thinkingLevel?: ThinkingLevel;\n error?: \"invalid_spec\";\n}\n\nexport interface ParsedSandboxCommand {\n action?: \"boost\" | \"visibility\";\n visibility?: string;\n}\n\nexport type ParsedLoginCommand =\n | { action: \"setup\" }\n | { action: \"shared_create\" | \"shared_update\" | \"shared_delete\"; name: string }\n | { action: \"shared_list\" }\n | { action: \"copy_shared\"; name: string };\n"]}
|
|
1
|
+
{"version":3,"file":"types.js","sourceRoot":"","sources":["../../../src/adapters/commands/types.ts"],"names":[],"mappings":"","sourcesContent":["import type { ThinkingLevel } from \"@earendil-works/pi-agent-core\";\nimport type { Api, Model } from \"@earendil-works/pi-ai\";\n\nimport type { Workspace } from \"../../office/types.js\";\nimport type { DockerContainerManager } from \"../../sandbox/provisioner.js\";\nimport type { SandboxConfig } from \"../../sandbox/types.js\";\nimport type {\n SandboxResourceController,\n ConversationContext,\n CompactOutcome,\n HandleCompactCommandOptions,\n HandleNewCommandOptions,\n MessagingInfo,\n MessagingBot,\n OfficeAddress,\n PlatformName,\n RunnerCacheControl,\n SessionViewTokenStoreLike,\n DesktopTokenStoreLike,\n OpenDesktop,\n} from \"../../types.js\";\nimport type { VaultManager } from \"../../vault/types.js\";\nimport type { AdminTokenCreateOptions } from \"../web/admin/types.js\";\n\ninterface CommandArgSpec {\n name: string;\n description: string;\n required: boolean;\n}\n\nexport interface SlackSlashRoute {\n includeText?: boolean;\n thread?: boolean;\n privateCommand?: boolean;\n}\n\nexport interface CommandManifestEntry {\n name: string;\n description: string;\n aliases?: readonly string[];\n arg?: CommandArgSpec;\n bare?: boolean;\n magicWord?: boolean;\n slackCommand?: string;\n slackRoute?: SlackSlashRoute;\n discord?: boolean;\n telegramMenu?: { description?: string };\n telegramCommand?: boolean;\n}\n\nexport interface LinkTokenStoreLike {\n create(\n platform: PlatformName,\n platformUserId: string,\n conversationId: string,\n vaultId: string,\n providerId: string,\n ): { token: string };\n}\n\nexport interface AdminTokenStoreLike {\n create(options: AdminTokenCreateOptions): { token: string };\n}\n\ninterface CommandRuntimeBridge extends RunnerCacheControl {\n handleNewCommand(options: HandleNewCommandOptions): Promise<void>;\n handleCompactCommand(options: HandleCompactCommandOptions): Promise<CompactOutcome>;\n}\n\nexport interface CommandServices {\n workspace: Workspace;\n runtime?: CommandRuntimeBridge;\n sandbox: SandboxConfig;\n vaultManager: VaultManager;\n provisioner?: DockerContainerManager;\n resourceController?: SandboxResourceController;\n linkTokenStore: LinkTokenStoreLike;\n sessionViewTokenStore: SessionViewTokenStoreLike;\n desktopTokenStore?: DesktopTokenStoreLike;\n openDesktop?: OpenDesktop;\n adminTokenStore: AdminTokenStoreLike;\n portalBaseUrl?: string;\n}\n\nexport interface CommandContext {\n bot: MessagingBot;\n responder: ConversationContext[\"responder\"];\n platform: PlatformName;\n address: OfficeAddress;\n platformUserId: string;\n platformUserName?: string;\n conversationId: string;\n vaultConversationId?: string;\n sessionKey: string;\n commandText: string;\n privateConversation: boolean;\n messagingInfo: MessagingInfo;\n services: CommandServices;\n}\n\nexport interface CommandHandler {\n tryHandle(context: CommandContext): Promise<boolean>;\n}\n\nexport interface ModelRegistry {\n find(provider: string, modelId: string): Model<Api> | undefined;\n}\n\nexport interface ParsedModelCommand {\n provider?: string;\n model?: string;\n modelCandidate?: string;\n thinkingLevelCandidate?: string;\n thinkingLevel?: ThinkingLevel;\n error?: \"invalid_spec\";\n}\n\nexport interface ParsedSandboxCommand {\n action?: \"boost\" | \"visibility\";\n visibility?: string;\n}\n\nexport type ParsedLoginCommand =\n | { action: \"setup\" }\n | { action: \"shared_create\" | \"shared_update\" | \"shared_delete\"; name: string }\n | { action: \"shared_list\" }\n | { action: \"copy_shared\"; name: string };\n"]}
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
import type { SlackAppApi, SlackAppCustomization, SlackAppManifest, SlackAppSyncResult, SlackBotApi, SlackConfigTokens } from "./types.js";
|
|
2
|
+
export declare const SLACK_APP_CUSTOMIZATION_FILE = "slack.json";
|
|
3
|
+
export declare const SLACK_CONFIG_TOKEN_FILE = "slack-config-token.json";
|
|
4
|
+
export declare function slackBotNameProblem(name: string): string | undefined;
|
|
5
|
+
export declare function slackAppManifest(customization?: SlackAppCustomization): SlackAppManifest;
|
|
6
|
+
export declare function readSlackAppCustomization(stateDir: string): SlackAppCustomization;
|
|
7
|
+
export declare function slackAppCustomizationProblem(customization: SlackAppCustomization): string | undefined;
|
|
8
|
+
export declare function writeSlackAppCustomization(stateDir: string, customization: SlackAppCustomization): void;
|
|
9
|
+
export declare function writeConfigTokens(stateDir: string, tokens: SlackConfigTokens): void;
|
|
10
|
+
export declare function syncSlackApp(options: {
|
|
11
|
+
stateDir: string;
|
|
12
|
+
apps: SlackAppApi;
|
|
13
|
+
bot: SlackBotApi;
|
|
14
|
+
}): Promise<SlackAppSyncResult>;
|
|
15
|
+
export declare function describeSlackAppSync(result: SlackAppSyncResult): string | undefined;
|
|
16
|
+
export declare function createSlackApp(options: {
|
|
17
|
+
apps: SlackAppApi;
|
|
18
|
+
refreshToken: string;
|
|
19
|
+
customization: SlackAppCustomization;
|
|
20
|
+
}): Promise<{
|
|
21
|
+
appId: string;
|
|
22
|
+
installUrl: string;
|
|
23
|
+
tokens: SlackConfigTokens;
|
|
24
|
+
}>;
|
|
25
|
+
export declare function slackAppApi(): SlackAppApi;
|
|
26
|
+
export declare function slackBotApi(botToken: string): SlackBotApi;
|
|
27
|
+
//# sourceMappingURL=app-config.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"app-config.d.ts","sourceRoot":"","sources":["../../../src/adapters/slack/app-config.ts"],"names":[],"mappings":"AAOA,OAAO,KAAK,EACV,WAAW,EACX,qBAAqB,EACrB,gBAAgB,EAChB,kBAAkB,EAClB,WAAW,EACX,iBAAiB,EAClB,MAAM,YAAY,CAAC;AAEpB,eAAO,MAAM,4BAA4B,eAAe,CAAC;AACzD,eAAO,MAAM,uBAAuB,4BAA4B,CAAC;AAqDjE,wBAAgB,mBAAmB,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,GAAG,SAAS,CAIpE;AAOD,wBAAgB,gBAAgB,CAAC,aAAa,GAAE,qBAA0B,GAAG,gBAAgB,CA4C5F;AAED,wBAAgB,yBAAyB,CAAC,QAAQ,EAAE,MAAM,GAAG,qBAAqB,CASjF;AAED,wBAAgB,4BAA4B,CAC1C,aAAa,EAAE,qBAAqB,GACnC,MAAM,GAAG,SAAS,CAIpB;AAED,wBAAgB,0BAA0B,CACxC,QAAQ,EAAE,MAAM,EAChB,aAAa,EAAE,qBAAqB,GACnC,IAAI,CAON;AAYD,wBAAgB,iBAAiB,CAAC,QAAQ,EAAE,MAAM,EAAE,MAAM,EAAE,iBAAiB,GAAG,IAAI,CAKnF;AAoCD,wBAAsB,YAAY,CAAC,OAAO,EAAE;IAC1C,QAAQ,EAAE,MAAM,CAAC;IACjB,IAAI,EAAE,WAAW,CAAC;IAClB,GAAG,EAAE,WAAW,CAAC;CAClB,GAAG,OAAO,CAAC,kBAAkB,CAAC,CAwB9B;AAED,wBAAgB,oBAAoB,CAAC,MAAM,EAAE,kBAAkB,GAAG,MAAM,GAAG,SAAS,CAanF;AAED,wBAAsB,cAAc,CAAC,OAAO,EAAE;IAC5C,IAAI,EAAE,WAAW,CAAC;IAClB,YAAY,EAAE,MAAM,CAAC;IACrB,aAAa,EAAE,qBAAqB,CAAC;CACtC,GAAG,OAAO,CAAC;IAAE,KAAK,EAAE,MAAM,CAAC;IAAC,UAAU,EAAE,MAAM,CAAC;IAAC,MAAM,EAAE,iBAAiB,CAAA;CAAE,CAAC,CAI5E;AASD,wBAAgB,WAAW,IAAI,WAAW,CAgCzC;AAED,wBAAgB,WAAW,CAAC,QAAQ,EAAE,MAAM,GAAG,WAAW,CAYzD"}
|
|
@@ -0,0 +1,241 @@
|
|
|
1
|
+
import { join } from "node:path";
|
|
2
|
+
import { WebClient } from "@slack/web-api";
|
|
3
|
+
import { Type } from "typebox";
|
|
4
|
+
import { Value } from "typebox/value";
|
|
5
|
+
import { COMMAND_MANIFEST } from "../commands/manifest.js";
|
|
6
|
+
import { atomicWritePrivateFile, readJsonSchemaFileIfExists } from "../../file-guards.js";
|
|
7
|
+
import { errorMessage, isRecord } from "../../unknown-values.js";
|
|
8
|
+
export const SLACK_APP_CUSTOMIZATION_FILE = "slack.json";
|
|
9
|
+
export const SLACK_CONFIG_TOKEN_FILE = "slack-config-token.json";
|
|
10
|
+
const BOT_SCOPES = [
|
|
11
|
+
"app_mentions:read",
|
|
12
|
+
"channels:history",
|
|
13
|
+
"channels:read",
|
|
14
|
+
"chat:write",
|
|
15
|
+
"chat:write.customize",
|
|
16
|
+
"commands",
|
|
17
|
+
"files:read",
|
|
18
|
+
"files:write",
|
|
19
|
+
"groups:history",
|
|
20
|
+
"groups:read",
|
|
21
|
+
"im:history",
|
|
22
|
+
"im:read",
|
|
23
|
+
"im:write",
|
|
24
|
+
"reactions:write",
|
|
25
|
+
"users:read",
|
|
26
|
+
];
|
|
27
|
+
const BOT_EVENTS = [
|
|
28
|
+
"app_home_opened",
|
|
29
|
+
"app_mention",
|
|
30
|
+
"message.channels",
|
|
31
|
+
"message.groups",
|
|
32
|
+
"message.im",
|
|
33
|
+
];
|
|
34
|
+
const SlackAppCustomizationSchema = Type.Object({
|
|
35
|
+
display_information: Type.Optional(Type.Object({
|
|
36
|
+
name: Type.Optional(Type.String({ minLength: 1, maxLength: 35 })),
|
|
37
|
+
description: Type.Optional(Type.String({ maxLength: 140 })),
|
|
38
|
+
long_description: Type.Optional(Type.String({ maxLength: 4000 })),
|
|
39
|
+
background_color: Type.Optional(Type.String({ pattern: "^#[0-9a-fA-F]{6}$" })),
|
|
40
|
+
}, { additionalProperties: false })),
|
|
41
|
+
}, { additionalProperties: false });
|
|
42
|
+
const ConfigTokenFileSchema = Type.Object({
|
|
43
|
+
token: Type.String({ minLength: 1 }),
|
|
44
|
+
refresh_token: Type.String({ minLength: 1 }),
|
|
45
|
+
});
|
|
46
|
+
const DEFAULT_BOT_NAME = "mikan";
|
|
47
|
+
const SLACK_BOT_NAME = /^[A-Za-z0-9 ._-]{1,80}$/;
|
|
48
|
+
export function slackBotNameProblem(name) {
|
|
49
|
+
return SLACK_BOT_NAME.test(name)
|
|
50
|
+
? undefined
|
|
51
|
+
: `Slack names the bot user only with letters, digits, spaces, ".", "_", and "-" (up to 80), so the bot keeps the name ${DEFAULT_BOT_NAME}.`;
|
|
52
|
+
}
|
|
53
|
+
function botName(customization) {
|
|
54
|
+
const name = customization.display_information?.name;
|
|
55
|
+
return name !== undefined && slackBotNameProblem(name) === undefined ? name : DEFAULT_BOT_NAME;
|
|
56
|
+
}
|
|
57
|
+
export function slackAppManifest(customization = {}) {
|
|
58
|
+
const manifest = {
|
|
59
|
+
display_information: {
|
|
60
|
+
name: "mikan",
|
|
61
|
+
description: "mikan Slack assistant",
|
|
62
|
+
background_color: "#4d5673",
|
|
63
|
+
...customization.display_information,
|
|
64
|
+
},
|
|
65
|
+
features: {
|
|
66
|
+
app_home: {
|
|
67
|
+
home_tab_enabled: true,
|
|
68
|
+
messages_tab_enabled: true,
|
|
69
|
+
messages_tab_read_only_enabled: false,
|
|
70
|
+
},
|
|
71
|
+
bot_user: { display_name: botName(customization), always_online: true },
|
|
72
|
+
slash_commands: COMMAND_MANIFEST.flatMap((entry) => entry.slackCommand
|
|
73
|
+
? [
|
|
74
|
+
{
|
|
75
|
+
command: entry.slackCommand,
|
|
76
|
+
description: entry.description,
|
|
77
|
+
usage_hint: entry.arg?.description,
|
|
78
|
+
should_escape: false,
|
|
79
|
+
},
|
|
80
|
+
]
|
|
81
|
+
: []),
|
|
82
|
+
},
|
|
83
|
+
oauth_config: {
|
|
84
|
+
redirect_urls: ["https://localhost"],
|
|
85
|
+
scopes: { bot: [...BOT_SCOPES] },
|
|
86
|
+
pkce_enabled: false,
|
|
87
|
+
},
|
|
88
|
+
settings: {
|
|
89
|
+
event_subscriptions: { bot_events: [...BOT_EVENTS] },
|
|
90
|
+
interactivity: { is_enabled: true },
|
|
91
|
+
org_deploy_enabled: false,
|
|
92
|
+
socket_mode_enabled: true,
|
|
93
|
+
token_rotation_enabled: false,
|
|
94
|
+
app_level_token_rotation_enabled: false,
|
|
95
|
+
is_mcp_enabled: false,
|
|
96
|
+
},
|
|
97
|
+
};
|
|
98
|
+
return manifest;
|
|
99
|
+
}
|
|
100
|
+
export function readSlackAppCustomization(stateDir) {
|
|
101
|
+
const path = join(stateDir, SLACK_APP_CUSTOMIZATION_FILE);
|
|
102
|
+
return (readJsonSchemaFileIfExists(path, SlackAppCustomizationSchema, (detail) => `Invalid ${path}: ${detail}. It may hold only display_information fields.`) ?? {});
|
|
103
|
+
}
|
|
104
|
+
export function slackAppCustomizationProblem(customization) {
|
|
105
|
+
if (Value.Check(SlackAppCustomizationSchema, customization))
|
|
106
|
+
return undefined;
|
|
107
|
+
const first = Value.Errors(SlackAppCustomizationSchema, customization)[0];
|
|
108
|
+
return `Slack would reject this app setting: ${first?.instancePath ?? ""} ${first?.message ?? ""}`.trim();
|
|
109
|
+
}
|
|
110
|
+
export function writeSlackAppCustomization(stateDir, customization) {
|
|
111
|
+
const problem = slackAppCustomizationProblem(customization);
|
|
112
|
+
if (problem)
|
|
113
|
+
throw new Error(problem);
|
|
114
|
+
atomicWritePrivateFile(join(stateDir, SLACK_APP_CUSTOMIZATION_FILE), `${JSON.stringify(customization, null, 2)}\n`);
|
|
115
|
+
}
|
|
116
|
+
function readConfigTokens(stateDir) {
|
|
117
|
+
const path = join(stateDir, SLACK_CONFIG_TOKEN_FILE);
|
|
118
|
+
const stored = readJsonSchemaFileIfExists(path, ConfigTokenFileSchema, (detail) => `Invalid ${path}: ${detail}`);
|
|
119
|
+
return stored ? { token: stored.token, refreshToken: stored.refresh_token } : undefined;
|
|
120
|
+
}
|
|
121
|
+
export function writeConfigTokens(stateDir, tokens) {
|
|
122
|
+
atomicWritePrivateFile(join(stateDir, SLACK_CONFIG_TOKEN_FILE), `${JSON.stringify({ token: tokens.token, refresh_token: tokens.refreshToken })}\n`);
|
|
123
|
+
}
|
|
124
|
+
function canonical(value) {
|
|
125
|
+
if (Array.isArray(value))
|
|
126
|
+
return value.map(canonical);
|
|
127
|
+
if (!isRecord(value))
|
|
128
|
+
return value;
|
|
129
|
+
return Object.fromEntries(Object.keys(value)
|
|
130
|
+
.filter((key) => value[key] !== undefined)
|
|
131
|
+
.toSorted()
|
|
132
|
+
.map((key) => [key, canonical(value[key])]));
|
|
133
|
+
}
|
|
134
|
+
const SLACK_MANAGED_APP_HOME_FIELDS = new Set(["agent_tasks_enabled"]);
|
|
135
|
+
function withoutSlackManagedFields(live) {
|
|
136
|
+
if (!isRecord(live) || !isRecord(live.features) || !isRecord(live.features.app_home))
|
|
137
|
+
return live;
|
|
138
|
+
const appHome = Object.fromEntries(Object.entries(live.features.app_home).filter(([key]) => !SLACK_MANAGED_APP_HOME_FIELDS.has(key)));
|
|
139
|
+
return { ...live, features: { ...live.features, app_home: appHome } };
|
|
140
|
+
}
|
|
141
|
+
function sameManifest(live, desired) {
|
|
142
|
+
return (JSON.stringify(canonical(withoutSlackManagedFields(live))) ===
|
|
143
|
+
JSON.stringify(canonical(desired)));
|
|
144
|
+
}
|
|
145
|
+
function reinstallMessage(appId, missingScopes) {
|
|
146
|
+
return `mikan updated this Slack app, and it now needs permissions the installed bot does not have: ${missingScopes.join(", ")}. Reinstall the app to grant them: https://api.slack.com/apps/${appId}/install-on-team`;
|
|
147
|
+
}
|
|
148
|
+
export async function syncSlackApp(options) {
|
|
149
|
+
const { stateDir, apps, bot } = options;
|
|
150
|
+
const stored = readConfigTokens(stateDir);
|
|
151
|
+
if (!stored)
|
|
152
|
+
return { status: "no-token" };
|
|
153
|
+
try {
|
|
154
|
+
const rotated = await apps.rotate(stored.refreshToken);
|
|
155
|
+
writeConfigTokens(stateDir, rotated);
|
|
156
|
+
const appId = await bot.appId();
|
|
157
|
+
const desired = slackAppManifest(readSlackAppCustomization(stateDir));
|
|
158
|
+
if (sameManifest(await apps.export(rotated.token, appId), desired)) {
|
|
159
|
+
return { status: "unchanged", appId };
|
|
160
|
+
}
|
|
161
|
+
const { permissionsUpdated } = await apps.update(rotated.token, appId, desired);
|
|
162
|
+
const granted = permissionsUpdated ? new Set(await bot.scopes()) : undefined;
|
|
163
|
+
const missingScopes = granted
|
|
164
|
+
? (desired.oauth_config?.scopes?.bot ?? []).filter((scope) => !granted.has(scope))
|
|
165
|
+
: [];
|
|
166
|
+
if (missingScopes.length > 0) {
|
|
167
|
+
await bot.directMessage(rotated.userId, reinstallMessage(appId, missingScopes));
|
|
168
|
+
}
|
|
169
|
+
return { status: "updated", appId, missingScopes };
|
|
170
|
+
}
|
|
171
|
+
catch (error) {
|
|
172
|
+
return { status: "failed", reason: errorMessage(error) };
|
|
173
|
+
}
|
|
174
|
+
}
|
|
175
|
+
export function describeSlackAppSync(result) {
|
|
176
|
+
switch (result.status) {
|
|
177
|
+
case "no-token":
|
|
178
|
+
return undefined;
|
|
179
|
+
case "failed":
|
|
180
|
+
return `Slack app sync failed, app left as it was: ${result.reason}`;
|
|
181
|
+
case "unchanged":
|
|
182
|
+
return `Slack app ${result.appId} already matches mikan's manifest`;
|
|
183
|
+
case "updated":
|
|
184
|
+
return result.missingScopes.length > 0
|
|
185
|
+
? `Slack app ${result.appId} updated to mikan's manifest; it needs a reinstall to grant ${result.missingScopes.join(", ")}, and the configuration token owner was asked by DM`
|
|
186
|
+
: `Slack app ${result.appId} updated to mikan's manifest`;
|
|
187
|
+
}
|
|
188
|
+
}
|
|
189
|
+
export async function createSlackApp(options) {
|
|
190
|
+
const tokens = await options.apps.rotate(options.refreshToken);
|
|
191
|
+
const created = await options.apps.create(tokens.token, slackAppManifest(options.customization));
|
|
192
|
+
return { ...created, tokens };
|
|
193
|
+
}
|
|
194
|
+
function required(value, what) {
|
|
195
|
+
if (value === undefined)
|
|
196
|
+
throw new Error(`Slack did not return ${what}`);
|
|
197
|
+
return value;
|
|
198
|
+
}
|
|
199
|
+
const NO_RETRIES = { retryConfig: { retries: 0 }, timeout: 15_000 };
|
|
200
|
+
export function slackAppApi() {
|
|
201
|
+
const client = new WebClient(undefined, NO_RETRIES);
|
|
202
|
+
return {
|
|
203
|
+
rotate: async (refreshToken) => {
|
|
204
|
+
const result = await client.tooling.tokens.rotate({ refresh_token: refreshToken });
|
|
205
|
+
return {
|
|
206
|
+
token: required(result.token, "a configuration token"),
|
|
207
|
+
refreshToken: required(result.refresh_token, "a refresh token"),
|
|
208
|
+
userId: required(result.user_id, "the token owner"),
|
|
209
|
+
};
|
|
210
|
+
},
|
|
211
|
+
export: async (token, appId) => required((await client.apps.manifest.export({ token, app_id: appId })).manifest, "a manifest"),
|
|
212
|
+
update: async (token, appId, manifest) => ({
|
|
213
|
+
permissionsUpdated: (await client.apps.manifest.update({ token, app_id: appId, manifest }))
|
|
214
|
+
.permissions_updated === true,
|
|
215
|
+
}),
|
|
216
|
+
create: async (token, manifest) => {
|
|
217
|
+
const result = await client.apps.manifest.create({ token, manifest });
|
|
218
|
+
return {
|
|
219
|
+
appId: required(result.app_id, "an app ID"),
|
|
220
|
+
installUrl: required(result.oauth_authorize_url, "an install link"),
|
|
221
|
+
};
|
|
222
|
+
},
|
|
223
|
+
delete: async (token, appId) => {
|
|
224
|
+
await client.apps.manifest.delete({ token, app_id: appId });
|
|
225
|
+
},
|
|
226
|
+
};
|
|
227
|
+
}
|
|
228
|
+
export function slackBotApi(botToken) {
|
|
229
|
+
const client = new WebClient(botToken, NO_RETRIES);
|
|
230
|
+
return {
|
|
231
|
+
appId: async () => {
|
|
232
|
+
const bot = required((await client.auth.test()).bot_id, "the bot ID");
|
|
233
|
+
return required((await client.bots.info({ bot })).bot?.app_id, "the app ID");
|
|
234
|
+
},
|
|
235
|
+
scopes: async () => (await client.auth.test()).response_metadata?.scopes ?? [],
|
|
236
|
+
directMessage: async (userId, text) => {
|
|
237
|
+
await client.chat.postMessage({ channel: userId, text });
|
|
238
|
+
},
|
|
239
|
+
};
|
|
240
|
+
}
|
|
241
|
+
//# sourceMappingURL=app-config.js.map
|