@cursor/july 0.1.52 → 0.1.53
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/AGENTS.md +1 -0
- package/README.md +4 -2
- package/dist/bin/agent-serve.js +2 -0
- package/dist/channels/slack/init.d.ts +16 -3
- package/dist/channels/slack/init.d.ts.map +1 -1
- package/dist/channels/slack/init.js +92 -18
- package/dist/channels/slack/manifest.d.ts +8 -0
- package/dist/channels/slack/manifest.d.ts.map +1 -1
- package/dist/channels/slack/manifest.js +17 -0
- package/dist/channels/slack/setup.d.ts +26 -1
- package/dist/channels/slack/setup.d.ts.map +1 -1
- package/dist/channels/slack/setup.js +107 -25
- package/dist/docs/404.html +3 -3
- package/dist/docs/ab.html +6 -6
- package/dist/docs/assets/{app.Cbeq6FOy.js → app.DAKcDclS.js} +1 -1
- package/dist/docs/assets/chunks/@localSearchIndexroot.C4uxzYEU.js +1 -0
- package/dist/docs/assets/chunks/{VPLocalSearchBox.Bmb8QjCI.js → VPLocalSearchBox.BNIaCSfG.js} +2 -2
- package/dist/docs/assets/chunks/{theme.BLNMeCRL.js → theme.B47GzJjc.js} +2 -2
- package/dist/docs/assets/{guides_slack.md.D2nTRu9M.js → guides_slack.md.DP4H75WP.js} +8 -6
- package/dist/docs/assets/guides_slack.md.DP4H75WP.lean.js +1 -0
- package/dist/docs/assets/{reference_cli.md.CGKfz_Yj.js → reference_cli.md.-V6J7Cl8.js} +7 -3
- package/dist/docs/assets/{reference_cli.md.CGKfz_Yj.lean.js → reference_cli.md.-V6J7Cl8.lean.js} +1 -1
- package/dist/docs/assets/{style.CK3hwt6q.css → style.kTsvp4pE.css} +1 -1
- package/dist/docs/assets/{troubleshooting.md.C6n3wCr6.js → troubleshooting.md.CuVztCKs.js} +1 -1
- package/dist/docs/building-with-agents.html +6 -6
- package/dist/docs/concepts.html +6 -6
- package/dist/docs/deployment.html +6 -6
- package/dist/docs/evals.html +6 -6
- package/dist/docs/example-agents/approval-buddy.html +6 -6
- package/dist/docs/example-agents/benny.html +6 -6
- package/dist/docs/example-agents/bugbot.html +6 -6
- package/dist/docs/example-agents/codebase-wiki.html +6 -6
- package/dist/docs/example-agents/codeowners-review.html +6 -6
- package/dist/docs/example-agents/concierge.html +6 -6
- package/dist/docs/example-agents/fsd.html +6 -6
- package/dist/docs/example-agents/index.html +6 -6
- package/dist/docs/example-agents/knowledge-base.html +6 -6
- package/dist/docs/example-agents/oncall.html +6 -6
- package/dist/docs/example-agents/security-reviewer.html +6 -6
- package/dist/docs/example-agents/slack-agent.html +6 -6
- package/dist/docs/example-agents/weather-agent.html +6 -6
- package/dist/docs/guides/agent-to-agent.html +6 -6
- package/dist/docs/guides/cloud-runtime.html +6 -6
- package/dist/docs/guides/github.html +6 -6
- package/dist/docs/guides/human-in-the-loop.html +6 -6
- package/dist/docs/guides/mcp-oauth.html +6 -6
- package/dist/docs/guides/opentelemetry.html +6 -6
- package/dist/docs/guides/slack.html +14 -12
- package/dist/docs/guides/webhooks.html +6 -6
- package/dist/docs/hashmap.json +1 -1
- package/dist/docs/hillclimbing.html +6 -6
- package/dist/docs/index.html +6 -6
- package/dist/docs/quickstart.html +6 -6
- package/dist/docs/reference/agent-config.html +6 -6
- package/dist/docs/reference/artifacts.html +6 -6
- package/dist/docs/reference/channels.html +6 -6
- package/dist/docs/reference/cli.html +13 -9
- package/dist/docs/reference/connections.html +6 -6
- package/dist/docs/reference/hooks.html +6 -6
- package/dist/docs/reference/http-api.html +6 -6
- package/dist/docs/reference/instructions.html +6 -6
- package/dist/docs/reference/playground.html +5 -5
- package/dist/docs/reference/project-layout.html +6 -6
- package/dist/docs/reference/prompt.html +6 -6
- package/dist/docs/reference/schedules.html +6 -6
- package/dist/docs/reference/sessions.html +6 -6
- package/dist/docs/reference/skills.html +6 -6
- package/dist/docs/reference/subagents.html +6 -6
- package/dist/docs/reference/tools.html +6 -6
- package/dist/docs/scaffolding-agents.html +5 -5
- package/dist/docs/storage.html +6 -6
- package/dist/docs/templates/triage.html +6 -6
- package/dist/docs/troubleshooting.html +6 -6
- package/dist/internal/cli-deploy.d.ts.map +1 -1
- package/dist/internal/cli-deploy.js +8 -18
- package/dist/internal/cli-slack-manual.d.ts +20 -0
- package/dist/internal/cli-slack-manual.d.ts.map +1 -0
- package/dist/internal/cli-slack-manual.js +106 -0
- package/dist/internal/cli-slack.d.ts +5 -2
- package/dist/internal/cli-slack.d.ts.map +1 -1
- package/dist/internal/cli-slack.js +18 -69
- package/dist/internal/deploy-client.d.ts +7 -10
- package/dist/internal/deploy-client.d.ts.map +1 -1
- package/dist/internal/deploy-client.js +1 -3
- package/dist/internal/deploy-source.d.ts +1 -1
- package/dist/internal/deploy-source.js +1 -1
- package/dist/internal/process-memory-telemetry.d.ts +3 -4
- package/dist/internal/process-memory-telemetry.d.ts.map +1 -1
- package/dist/internal/process-memory-telemetry.js +4 -17
- package/dist/playground/assets/{index-BKMz70wx.js → index-BHAPd9KT.js} +31 -31
- package/dist/playground/assets/index-DpKH0HcT.css +1 -0
- package/dist/playground/index.html +2 -2
- package/docs/guides/slack.md +52 -7
- package/docs/reference/cli.md +29 -6
- package/docs/troubleshooting.md +1 -1
- package/package.json +1 -1
- package/skills/create-agent/SKILL.md +1 -0
- package/skills/setup-slack/SKILL.md +99 -7
- package/src/bin/agent-serve.ts +2 -0
- package/src/channels/slack/init.ts +130 -20
- package/src/channels/slack/manifest.ts +27 -0
- package/src/channels/slack/setup.ts +124 -23
- package/src/internal/cli-deploy.ts +8 -22
- package/src/internal/cli-slack-manual.ts +127 -0
- package/src/internal/cli-slack.ts +18 -70
- package/src/internal/deploy-client.ts +8 -13
- package/src/internal/deploy-source.ts +1 -1
- package/src/internal/process-memory-telemetry.ts +5 -24
- package/dist/docs/assets/chunks/@localSearchIndexroot.TzAgoWLl.js +0 -1
- package/dist/docs/assets/guides_slack.md.D2nTRu9M.lean.js +0 -1
- package/dist/playground/assets/index-BmMrulSc.css +0 -1
- /package/dist/docs/assets/{troubleshooting.md.C6n3wCr6.lean.js → troubleshooting.md.CuVztCKs.lean.js} +0 -0
|
@@ -2,32 +2,133 @@
|
|
|
2
2
|
* Slack setup guide printed by `agent-serve slack setup`.
|
|
3
3
|
*
|
|
4
4
|
* Two products: Cursor-hosted (`cursorAccount`) vs a dedicated Socket
|
|
5
|
-
* Mode app via `slack create` (dashboard wizard).
|
|
5
|
+
* Mode app via `slack create` (dashboard wizard). `slack init --manual`
|
|
6
|
+
* is the unmanaged paste-at-api.slack.com path. {@link SLACK_SETUP_PHASES}
|
|
7
|
+
* is that checklist; its ids key `.agent-serve/slack/setup-status.json`.
|
|
6
8
|
*/
|
|
7
9
|
|
|
8
|
-
export
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
slackChannel({ cursorAccount: true, agentName: "MyAgent" })
|
|
15
|
-
Sign in (agent-serve login / CURSOR_API_KEY), serve, then mention
|
|
16
|
-
"@Cursor MyAgent ..." in the linked workspace. No app, no tokens.
|
|
17
|
-
|
|
18
|
-
B) Dedicated Slack app (Socket Mode):
|
|
19
|
-
agent-serve slack create --dir .
|
|
20
|
-
Opens the signed-in Cursor dashboard wizard. Finish Slack consent
|
|
21
|
-
and the bot name there. The CLI writes tokens into .env.local.
|
|
22
|
-
Use this when you need your own bot user, channel watching
|
|
23
|
-
(engagement.channelPosts), or approval buttons.
|
|
10
|
+
export interface SlackSetupPhase {
|
|
11
|
+
id: string;
|
|
12
|
+
actor: "agent" | "human";
|
|
13
|
+
title: string;
|
|
14
|
+
steps: string[];
|
|
15
|
+
}
|
|
24
16
|
|
|
25
|
-
|
|
17
|
+
export const SLACK_SETUP_PHASES: SlackSetupPhase[] = [
|
|
18
|
+
{
|
|
19
|
+
id: "generate",
|
|
20
|
+
actor: "agent",
|
|
21
|
+
title: "Generate channel + manifests",
|
|
22
|
+
steps: [
|
|
23
|
+
"agent-serve slack init --manual --dir . --name My Agent",
|
|
24
|
+
"Manifests default to Socket Mode (no public Request URL).",
|
|
25
|
+
],
|
|
26
|
+
},
|
|
27
|
+
{
|
|
28
|
+
id: "createApps",
|
|
29
|
+
actor: "human",
|
|
30
|
+
title: "Create two Slack apps from manifests",
|
|
31
|
+
steps: [
|
|
32
|
+
"Open https://api.slack.com/apps -> Create New App from a manifest",
|
|
33
|
+
"Paste .agent-serve/slack/manifest.dev.json -> create the *dev* app",
|
|
34
|
+
"Repeat with manifest.prod.json for the *prod* app",
|
|
35
|
+
],
|
|
36
|
+
},
|
|
37
|
+
{
|
|
38
|
+
id: "install",
|
|
39
|
+
actor: "human",
|
|
40
|
+
title: "Install + mint App-Level Token",
|
|
41
|
+
steps: [
|
|
42
|
+
"Install to Workspace; copy Bot User OAuth Token (xoxb-...)",
|
|
43
|
+
"App-Level Tokens: create token with connections:write; copy xapp-...",
|
|
44
|
+
],
|
|
45
|
+
},
|
|
46
|
+
{
|
|
47
|
+
id: "env",
|
|
48
|
+
actor: "agent",
|
|
49
|
+
title: "Wire environment",
|
|
50
|
+
steps: [
|
|
51
|
+
"Write per-agent tokens into .env.local (see .agent-serve/slack/env.example)",
|
|
52
|
+
"Multi-agent: <PREFIX>_SLACK_BOT_TOKEN + <PREFIX>_SLACK_APP_TOKEN",
|
|
53
|
+
"Single agent: SLACK_BOT_TOKEN + SLACK_APP_TOKEN (--no-prefix)",
|
|
54
|
+
"agent-serve slack doctor --prefix <PREFIX>",
|
|
55
|
+
],
|
|
56
|
+
},
|
|
57
|
+
{
|
|
58
|
+
id: "serve",
|
|
59
|
+
actor: "agent",
|
|
60
|
+
title: "Serve",
|
|
61
|
+
steps: [
|
|
62
|
+
"agent-serve serve --dir . --dev",
|
|
63
|
+
"Confirm log: [agent-sdk/slack] Socket Mode connected",
|
|
64
|
+
],
|
|
65
|
+
},
|
|
66
|
+
{
|
|
67
|
+
id: "smoke",
|
|
68
|
+
actor: "human",
|
|
69
|
+
title: "Smoke test",
|
|
70
|
+
steps: [
|
|
71
|
+
"Invite the bot to a channel",
|
|
72
|
+
"@mention it or DM it",
|
|
73
|
+
"Confirm logs: inbound → session start → reply delivered",
|
|
74
|
+
"Confirm streaming reply and rotating status",
|
|
75
|
+
],
|
|
76
|
+
},
|
|
77
|
+
];
|
|
26
78
|
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
79
|
+
/** Seed `.agent-serve/slack/setup-status.json` after `slack init --manual`. */
|
|
80
|
+
export function initialSlackSetupStatus(options: {
|
|
81
|
+
envPrefix: string | undefined;
|
|
82
|
+
envKeys: { bot: string; app: string };
|
|
83
|
+
}): {
|
|
84
|
+
updatedAt: string;
|
|
85
|
+
envPrefix: string | null;
|
|
86
|
+
envKeys: { bot: string; app: string };
|
|
87
|
+
phases: Record<string, boolean>;
|
|
88
|
+
} {
|
|
89
|
+
const completed = SLACK_SETUP_PHASES[0]?.id;
|
|
90
|
+
return {
|
|
91
|
+
updatedAt: new Date().toISOString(),
|
|
92
|
+
envPrefix: options.envPrefix ?? null,
|
|
93
|
+
envKeys: options.envKeys,
|
|
94
|
+
phases: Object.fromEntries(
|
|
95
|
+
SLACK_SETUP_PHASES.map((phase) => [phase.id, phase.id === completed])
|
|
96
|
+
),
|
|
97
|
+
};
|
|
98
|
+
}
|
|
30
99
|
|
|
31
|
-
|
|
32
|
-
|
|
100
|
+
export function formatSlackSetupGuide(): string {
|
|
101
|
+
const lines: string[] = [
|
|
102
|
+
"agent-serve — Slack setup",
|
|
103
|
+
"",
|
|
104
|
+
"Two products. Pick one.",
|
|
105
|
+
"",
|
|
106
|
+
" A) Cursor-hosted (recommended default, zero Slack admin):",
|
|
107
|
+
' slackChannel({ cursorAccount: true, agentName: "MyAgent" })',
|
|
108
|
+
" Sign in (agent-serve login / CURSOR_API_KEY), serve, then mention",
|
|
109
|
+
' "@Cursor MyAgent ..." in the linked workspace. No app, no tokens.',
|
|
110
|
+
" B) Dedicated Slack app (Socket Mode) via the dashboard wizard:",
|
|
111
|
+
" agent-serve slack create --dir .",
|
|
112
|
+
" Finish Slack consent and the bot name there. The CLI writes",
|
|
113
|
+
" tokens into .env.local. Use this when you need your own bot",
|
|
114
|
+
" user, channel watching, or approval buttons.",
|
|
115
|
+
"",
|
|
116
|
+
"Manual Slack app: `slack init --manual`. Phases below.",
|
|
117
|
+
"Goal: importable dev + prod Slack apps over Socket Mode (no public URL).",
|
|
118
|
+
"App-Level Token (xapp-...) + Bot Token (xoxb-...) required.",
|
|
119
|
+
"",
|
|
120
|
+
];
|
|
121
|
+
for (const [index, phase] of SLACK_SETUP_PHASES.entries()) {
|
|
122
|
+
lines.push(`## Phase ${index} — ${phase.title} (${phase.actor})`);
|
|
123
|
+
for (const step of phase.steps) {
|
|
124
|
+
lines.push(` - ${step}`);
|
|
125
|
+
}
|
|
126
|
+
lines.push("");
|
|
127
|
+
}
|
|
128
|
+
lines.push("Status file: .agent-serve/slack/setup-status.json");
|
|
129
|
+
lines.push(
|
|
130
|
+
"Skill (Cursor): packages/agent-serve/skills/setup-slack/SKILL.md"
|
|
131
|
+
);
|
|
132
|
+
lines.push("");
|
|
133
|
+
return lines.join("\n");
|
|
33
134
|
}
|
|
@@ -405,7 +405,7 @@ async function resolveDeployTargets(
|
|
|
405
405
|
);
|
|
406
406
|
return 1;
|
|
407
407
|
}
|
|
408
|
-
//
|
|
408
|
+
// Bare-slug deploy against a non-agent directory (still requires --repo).
|
|
409
409
|
let slug: string;
|
|
410
410
|
if (options.slug !== undefined) {
|
|
411
411
|
slug = options.slug;
|
|
@@ -419,9 +419,7 @@ async function resolveDeployTargets(
|
|
|
419
419
|
}
|
|
420
420
|
slug = derived;
|
|
421
421
|
if (!ctx.json) {
|
|
422
|
-
ctx.err(
|
|
423
|
-
`Deploying as "${slug}" (static; no agent project at ${dir}).\n`
|
|
424
|
-
);
|
|
422
|
+
ctx.err(`Deploying as "${slug}" (no agent project at ${dir}).\n`);
|
|
425
423
|
}
|
|
426
424
|
}
|
|
427
425
|
return [{ slug, dir }];
|
|
@@ -586,21 +584,10 @@ async function deployOne(
|
|
|
586
584
|
);
|
|
587
585
|
return 1;
|
|
588
586
|
}
|
|
589
|
-
|
|
590
|
-
// deploy body the control plane rejects. Fail here with a fix hint —
|
|
591
|
-
// common when origin is an unrecognized SSH shape (pass --repo).
|
|
592
|
-
if (
|
|
593
|
-
gitRepoUrl === undefined &&
|
|
594
|
-
(gitRef !== undefined ||
|
|
595
|
-
agentPath !== undefined ||
|
|
596
|
-
(options.cursorEventsRepos !== undefined &&
|
|
597
|
-
options.cursorEventsRepos.length > 0) ||
|
|
598
|
-
(options.allowDomains !== undefined && options.allowDomains.length > 0))
|
|
599
|
-
) {
|
|
587
|
+
if (gitRepoUrl === undefined) {
|
|
600
588
|
ctx.err(
|
|
601
589
|
`Could not resolve an https:// git repository URL for this deploy.\n` +
|
|
602
|
-
` Pass --repo https://github.com/<owner>/<repo> (inferred from origin when possible).\n`
|
|
603
|
-
` Or omit --ref/--path/--cursor-events-repo/--allow-domain for a static engine.\n`
|
|
590
|
+
` Pass --repo https://github.com/<owner>/<repo> (inferred from origin when possible).\n`
|
|
604
591
|
);
|
|
605
592
|
return 1;
|
|
606
593
|
}
|
|
@@ -633,8 +620,7 @@ async function deployOne(
|
|
|
633
620
|
|
|
634
621
|
// The package manifest (`defineAgent({ hosting })`) is the source of
|
|
635
622
|
// truth for egress domains and expected secret names; flags override or
|
|
636
|
-
// augment it. Directories without an agent project
|
|
637
|
-
// deploys) simply have no manifest. The same load supplies the
|
|
623
|
+
// augment it. Directories without an agent project have no manifest. The same load supplies the
|
|
638
624
|
// cursorAccount connection refs for the post-deploy connector auth and
|
|
639
625
|
// the channels declared public via `publicEndpoint()`.
|
|
640
626
|
const manifest = isAgent ? await readDeployManifest(dir) : undefined;
|
|
@@ -661,11 +647,11 @@ async function deployOne(
|
|
|
661
647
|
);
|
|
662
648
|
}
|
|
663
649
|
|
|
664
|
-
// Today's only source shape: the control plane builds from a git ref
|
|
665
|
-
//
|
|
650
|
+
// Today's only source shape: the control plane builds from a git ref.
|
|
651
|
+
// See DeploySource for the artifact seam.
|
|
666
652
|
const source: DeploySource = {
|
|
667
653
|
kind: "git-ref",
|
|
668
|
-
|
|
654
|
+
gitRepoUrl,
|
|
669
655
|
...(gitRef === undefined ? {} : { gitRef }),
|
|
670
656
|
...(agentPath === undefined ? {} : { agentPath }),
|
|
671
657
|
...(options.cursorEventsRepos === undefined ||
|
|
@@ -0,0 +1,127 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* `slack init --manual` and `slack manifest` — DIY paste-at-api.slack.com.
|
|
3
|
+
* Create/destroy/icon stay in `cli-slack.ts`.
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
import { mkdir, writeFile } from "node:fs/promises";
|
|
7
|
+
import { basename, join, resolve as resolvePath } from "node:path";
|
|
8
|
+
import {
|
|
9
|
+
buildSlackManifestPair,
|
|
10
|
+
initSlackManual,
|
|
11
|
+
slackManifestFileEntries,
|
|
12
|
+
} from "../channels/slack/index.js";
|
|
13
|
+
import { describeError } from "./describe-error.js";
|
|
14
|
+
import { CLI_COMMAND_NAME as CLI } from "./distribution.js";
|
|
15
|
+
|
|
16
|
+
export interface SlackManualCliOptions {
|
|
17
|
+
dir: string;
|
|
18
|
+
name?: string;
|
|
19
|
+
env?: "dev" | "prod" | "both";
|
|
20
|
+
print?: boolean;
|
|
21
|
+
prefix?: string;
|
|
22
|
+
noPrefix?: boolean;
|
|
23
|
+
channelPosts?: boolean;
|
|
24
|
+
/** Gate for `slack init`; ignored by `slack manifest`. */
|
|
25
|
+
manual?: boolean;
|
|
26
|
+
out?: (text: string) => void;
|
|
27
|
+
err?: (text: string) => void;
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
function errStream(options: SlackManualCliOptions): (text: string) => void {
|
|
31
|
+
return options.err ?? ((text: string) => void process.stderr.write(text));
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
export async function cmdSlackInit(
|
|
35
|
+
options: SlackManualCliOptions
|
|
36
|
+
): Promise<number> {
|
|
37
|
+
if (options.manual !== true) {
|
|
38
|
+
errStream(options)(
|
|
39
|
+
`\`slack init\` without --manual is not the first-run path. Socket Mode apps are created with:
|
|
40
|
+
|
|
41
|
+
${CLI} slack create --dir .
|
|
42
|
+
|
|
43
|
+
That opens the Cursor dashboard wizard.
|
|
44
|
+
|
|
45
|
+
To install a custom Slack app from a manifest (api.slack.com):
|
|
46
|
+
|
|
47
|
+
${CLI} slack init --manual --dir .
|
|
48
|
+
`
|
|
49
|
+
);
|
|
50
|
+
return 1;
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
const dir = resolvePath(options.dir);
|
|
54
|
+
const out =
|
|
55
|
+
options.out ?? ((text: string) => void process.stdout.write(text));
|
|
56
|
+
try {
|
|
57
|
+
const result = await initSlackManual({
|
|
58
|
+
dir,
|
|
59
|
+
name: options.name,
|
|
60
|
+
envPrefix: options.noPrefix === true ? "" : options.prefix,
|
|
61
|
+
channelPosts: options.channelPosts,
|
|
62
|
+
});
|
|
63
|
+
out(
|
|
64
|
+
`${JSON.stringify(
|
|
65
|
+
{
|
|
66
|
+
ok: true,
|
|
67
|
+
directory: result.rootDir,
|
|
68
|
+
created: result.files,
|
|
69
|
+
envPrefix: result.envPrefix ?? null,
|
|
70
|
+
envKeys: result.envKeys,
|
|
71
|
+
next: [
|
|
72
|
+
"Open https://api.slack.com/apps → Create from manifest",
|
|
73
|
+
"Import .agent-serve/slack/manifest.dev.json (then prod)",
|
|
74
|
+
`Install apps; set ${result.envKeys.bot} + ${result.envKeys.app}`,
|
|
75
|
+
result.envPrefix === undefined
|
|
76
|
+
? `${CLI} slack doctor`
|
|
77
|
+
: `${CLI} slack doctor --prefix ${result.envPrefix}`,
|
|
78
|
+
`${CLI} serve --dir . --dev`,
|
|
79
|
+
`${CLI} slack setup`,
|
|
80
|
+
],
|
|
81
|
+
},
|
|
82
|
+
null,
|
|
83
|
+
2
|
|
84
|
+
)}\n`
|
|
85
|
+
);
|
|
86
|
+
return 0;
|
|
87
|
+
} catch (error) {
|
|
88
|
+
errStream(options)(`${describeError(error)}\n`);
|
|
89
|
+
return 1;
|
|
90
|
+
}
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
export async function runSlackManifest(
|
|
94
|
+
options: SlackManualCliOptions
|
|
95
|
+
): Promise<number> {
|
|
96
|
+
const env = options.env ?? "both";
|
|
97
|
+
const dir = resolvePath(options.dir);
|
|
98
|
+
const pair = buildSlackManifestPair({
|
|
99
|
+
name: options.name ?? basename(dir),
|
|
100
|
+
managedByCursor: false,
|
|
101
|
+
channelPosts: options.channelPosts === true ? true : undefined,
|
|
102
|
+
});
|
|
103
|
+
|
|
104
|
+
if (options.print) {
|
|
105
|
+
if (env === "dev" || env === "both") {
|
|
106
|
+
process.stdout.write(`${JSON.stringify(pair.dev, null, 2)}\n`);
|
|
107
|
+
}
|
|
108
|
+
if (env === "prod" || env === "both") {
|
|
109
|
+
if (env === "both") {
|
|
110
|
+
process.stdout.write("--- prod ---\n");
|
|
111
|
+
}
|
|
112
|
+
process.stdout.write(`${JSON.stringify(pair.prod, null, 2)}\n`);
|
|
113
|
+
}
|
|
114
|
+
return 0;
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
const outDir = join(dir, ".agent-serve/slack");
|
|
118
|
+
await mkdir(outDir, { recursive: true });
|
|
119
|
+
const written: string[] = [];
|
|
120
|
+
for (const file of slackManifestFileEntries(pair, env)) {
|
|
121
|
+
const path = join(outDir, file.name);
|
|
122
|
+
await writeFile(path, file.contents);
|
|
123
|
+
written.push(path);
|
|
124
|
+
}
|
|
125
|
+
process.stdout.write(`${JSON.stringify({ ok: true, written }, null, 2)}\n`);
|
|
126
|
+
return 0;
|
|
127
|
+
}
|
|
@@ -1,12 +1,12 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* `agent-serve slack …` CLI — doctor, destroy/icon,
|
|
3
|
-
* dashboard-wizard launcher (`slack-provision-client.ts`)
|
|
2
|
+
* `agent-serve slack …` CLI — doctor, destroy/icon, `create` as a
|
|
3
|
+
* dashboard-wizard launcher (`slack-provision-client.ts`), and
|
|
4
|
+
* `init --manual` for hand-imported manifests.
|
|
4
5
|
*/
|
|
5
6
|
|
|
6
|
-
import { access, chmod,
|
|
7
|
+
import { access, chmod, readFile, writeFile } from "node:fs/promises";
|
|
7
8
|
import { basename, extname, join, resolve as resolvePath } from "node:path";
|
|
8
9
|
import {
|
|
9
|
-
buildSlackManifestPair,
|
|
10
10
|
envPrefixFromName,
|
|
11
11
|
formatSlackSetupGuide,
|
|
12
12
|
initSlackChannel,
|
|
@@ -15,6 +15,7 @@ import {
|
|
|
15
15
|
slackEnvKeys,
|
|
16
16
|
} from "../channels/slack/index.js";
|
|
17
17
|
import { resolveDeployAuth } from "./cli-deploy.js";
|
|
18
|
+
import { cmdSlackInit, runSlackManifest } from "./cli-slack-manual.js";
|
|
18
19
|
import { cursorExternalApiUrl } from "./cursor/credentials.js";
|
|
19
20
|
import { slugFromDirectoryName } from "./deploy-client.js";
|
|
20
21
|
import { inferDeployGitSource } from "./deploy-source.js";
|
|
@@ -58,6 +59,8 @@ export interface SlackCliOptions {
|
|
|
58
59
|
icon?: string;
|
|
59
60
|
/** create: accepted for compatibility; live-app updates always overwrite. */
|
|
60
61
|
force?: boolean;
|
|
62
|
+
/** `slack init --manual`: manifests for paste at api.slack.com. */
|
|
63
|
+
manual?: boolean;
|
|
61
64
|
/** `slack icon <source>` positional (same URL-or-file forms as --icon). */
|
|
62
65
|
iconSource?: string;
|
|
63
66
|
/** Test seams. */
|
|
@@ -81,6 +84,10 @@ Usage:
|
|
|
81
84
|
[--team <id>] [--json]
|
|
82
85
|
${CLI} slack icon <URL-or-FILE> [--dir .] [--slack-team T0123ABCD]
|
|
83
86
|
[--prod] [--team <id>] [--json]
|
|
87
|
+
${CLI} slack init --manual [--dir .] [--name NAME]
|
|
88
|
+
[--prefix PREFIX | --no-prefix] [--channel-posts]
|
|
89
|
+
${CLI} slack manifest --env dev|prod|both [--dir .] [--name NAME]
|
|
90
|
+
[--print]
|
|
84
91
|
${CLI} slack doctor [--dir .] [--prefix PREFIX | --no-prefix] [--json]
|
|
85
92
|
|
|
86
93
|
create/destroy/icon — dedicated Socket Mode app via the Cursor dashboard:
|
|
@@ -94,13 +101,14 @@ create/destroy/icon — dedicated Socket Mode app via the Cursor dashboard:
|
|
|
94
101
|
icon Set the provisioned app's icon: an https image URL or a local
|
|
95
102
|
png/jpg/gif file (at most 512KB).
|
|
96
103
|
|
|
97
|
-
\`slack init\` is
|
|
98
|
-
at
|
|
104
|
+
\`slack init\` without --manual is not the first-run path. Use \`slack create\`.
|
|
105
|
+
\`slack init --manual\` writes the channel file, manifests to paste at
|
|
106
|
+
api.slack.com, env.example, and setup-status.json.
|
|
99
107
|
|
|
100
108
|
Env tokens:
|
|
101
109
|
single agent: SLACK_BOT_TOKEN + SLACK_APP_TOKEN
|
|
102
110
|
multi-agent: <PREFIX>_SLACK_BOT_TOKEN + <PREFIX>_SLACK_APP_TOKEN
|
|
103
|
-
(create default PREFIX from the directory name;
|
|
111
|
+
(create / init --manual default PREFIX from the directory name;
|
|
104
112
|
doctor defaults to it too when those keys are set)
|
|
105
113
|
|
|
106
114
|
See also: skills/setup-slack/SKILL.md
|
|
@@ -129,7 +137,7 @@ export async function cmdSlack(
|
|
|
129
137
|
case "init":
|
|
130
138
|
return cmdSlackInit(options);
|
|
131
139
|
case "manifest":
|
|
132
|
-
return
|
|
140
|
+
return runSlackManifest(options);
|
|
133
141
|
case "doctor":
|
|
134
142
|
return runDoctor(options);
|
|
135
143
|
default:
|
|
@@ -139,58 +147,6 @@ export async function cmdSlack(
|
|
|
139
147
|
}
|
|
140
148
|
}
|
|
141
149
|
|
|
142
|
-
async function cmdSlackInit(options: SlackCliOptions): Promise<number> {
|
|
143
|
-
errStream(options)(
|
|
144
|
-
`\`slack init\` is removed. Socket Mode apps are created with:
|
|
145
|
-
|
|
146
|
-
${CLI} slack create --dir .
|
|
147
|
-
|
|
148
|
-
That opens the Cursor dashboard wizard. Do not paste a manifest at api.slack.com.
|
|
149
|
-
`
|
|
150
|
-
);
|
|
151
|
-
return 1;
|
|
152
|
-
}
|
|
153
|
-
|
|
154
|
-
async function runManifest(options: SlackCliOptions): Promise<number> {
|
|
155
|
-
const env = options.env ?? "both";
|
|
156
|
-
const dir = resolvePath(options.dir);
|
|
157
|
-
const name = options.name ?? basename(dir);
|
|
158
|
-
const pair = buildSlackManifestPair({
|
|
159
|
-
name,
|
|
160
|
-
managedByCursor: false,
|
|
161
|
-
channelPosts: options.channelPosts === true ? true : undefined,
|
|
162
|
-
});
|
|
163
|
-
|
|
164
|
-
if (options.print) {
|
|
165
|
-
if (env === "dev" || env === "both") {
|
|
166
|
-
process.stdout.write(`${JSON.stringify(pair.dev, null, 2)}\n`);
|
|
167
|
-
}
|
|
168
|
-
if (env === "prod" || env === "both") {
|
|
169
|
-
if (env === "both") {
|
|
170
|
-
process.stdout.write("--- prod ---\n");
|
|
171
|
-
}
|
|
172
|
-
process.stdout.write(`${JSON.stringify(pair.prod, null, 2)}\n`);
|
|
173
|
-
}
|
|
174
|
-
return 0;
|
|
175
|
-
}
|
|
176
|
-
|
|
177
|
-
const outDir = join(dir, ".agent-serve/slack");
|
|
178
|
-
await mkdir(outDir, { recursive: true });
|
|
179
|
-
const written: string[] = [];
|
|
180
|
-
if (env === "dev" || env === "both") {
|
|
181
|
-
const path = join(outDir, "manifest.dev.json");
|
|
182
|
-
await writeFile(path, `${JSON.stringify(pair.dev, null, 2)}\n`);
|
|
183
|
-
written.push(path);
|
|
184
|
-
}
|
|
185
|
-
if (env === "prod" || env === "both") {
|
|
186
|
-
const path = join(outDir, "manifest.prod.json");
|
|
187
|
-
await writeFile(path, `${JSON.stringify(pair.prod, null, 2)}\n`);
|
|
188
|
-
written.push(path);
|
|
189
|
-
}
|
|
190
|
-
process.stdout.write(`${JSON.stringify({ ok: true, written }, null, 2)}\n`);
|
|
191
|
-
return 0;
|
|
192
|
-
}
|
|
193
|
-
|
|
194
150
|
async function runDoctor(options: SlackCliOptions): Promise<number> {
|
|
195
151
|
const dir = resolvePath(options.dir);
|
|
196
152
|
// Tokens usually live in the agent's .env.local; real env still wins.
|
|
@@ -450,7 +406,7 @@ export async function cmdSlackCreate(
|
|
|
450
406
|
|
|
451
407
|
let wrote: string[];
|
|
452
408
|
try {
|
|
453
|
-
wrote = await scaffoldSlackChannelFiles(
|
|
409
|
+
wrote = await scaffoldSlackChannelFiles(target);
|
|
454
410
|
} catch (error) {
|
|
455
411
|
ctx.err(`${describeError(error)}\n`);
|
|
456
412
|
return 1;
|
|
@@ -780,7 +736,6 @@ function formatAppList(
|
|
|
780
736
|
* never overwritten: initSlackChannel writes with `wx`.
|
|
781
737
|
*/
|
|
782
738
|
async function scaffoldSlackChannelFiles(
|
|
783
|
-
options: SlackCliOptions,
|
|
784
739
|
target: SlackAppTarget
|
|
785
740
|
): Promise<string[]> {
|
|
786
741
|
const channelFile = "agent/channels/slack.ts";
|
|
@@ -788,22 +743,15 @@ async function scaffoldSlackChannelFiles(
|
|
|
788
743
|
return [];
|
|
789
744
|
}
|
|
790
745
|
try {
|
|
791
|
-
// Pass the resolved name/prefix so the channel file and the env keys
|
|
792
|
-
// share one derivation.
|
|
793
746
|
const result = await initSlackChannel({
|
|
794
747
|
dir: target.dir,
|
|
795
748
|
name: target.name,
|
|
796
749
|
envPrefix: target.envPrefix ?? "",
|
|
797
|
-
channelPosts: options.channelPosts,
|
|
798
750
|
});
|
|
799
751
|
return result.files;
|
|
800
752
|
} catch (error) {
|
|
801
753
|
if (isErrnoException(error) && error.code === "EEXIST") {
|
|
802
|
-
|
|
803
|
-
// later pre-existing file aborts the scaffold.
|
|
804
|
-
return (await fileExists(join(target.dir, channelFile)))
|
|
805
|
-
? [channelFile]
|
|
806
|
-
: [];
|
|
754
|
+
return [channelFile];
|
|
807
755
|
}
|
|
808
756
|
throw error;
|
|
809
757
|
}
|
|
@@ -52,29 +52,26 @@ export const TERMINAL_DEPLOY_STATUSES: ReadonlySet<DeploymentStatus> = new Set([
|
|
|
52
52
|
|
|
53
53
|
/**
|
|
54
54
|
* What gets deployed. Today the control plane builds the engine itself from
|
|
55
|
-
* a git ref
|
|
56
|
-
*
|
|
57
|
-
*
|
|
58
|
-
*
|
|
59
|
-
*
|
|
55
|
+
* a git ref, so the only member is the slug/git-ref shape. When
|
|
56
|
+
* artifact-based deploys ship ("build locally + upload"), add an
|
|
57
|
+
* `{ kind: "artifact", ... }` member here and a branch in
|
|
58
|
+
* {@link AgentServeDeployClient.deploy} — the command surface and everything
|
|
59
|
+
* downstream stays unchanged.
|
|
60
60
|
*/
|
|
61
61
|
export type DeploySource = {
|
|
62
62
|
kind: "git-ref";
|
|
63
|
-
|
|
64
|
-
gitRepoUrl?: string;
|
|
63
|
+
gitRepoUrl: string;
|
|
65
64
|
gitRef?: string;
|
|
66
65
|
agentPath?: string;
|
|
67
66
|
/** Repos whose SCM events the deployed engine pulls (`owner/name`). */
|
|
68
67
|
cursorEventRepos?: string[];
|
|
69
68
|
/**
|
|
70
|
-
* Egress allowlist for the engine pod.
|
|
71
|
-
* deploys — the backend 400s when set on a static deploy.
|
|
69
|
+
* Egress allowlist for the engine pod.
|
|
72
70
|
*/
|
|
73
71
|
egressAllowedDomains?: string[];
|
|
74
72
|
/**
|
|
75
73
|
* `/v1/channels/…` subtrees the stable alias serves WITHOUT
|
|
76
74
|
* `X-Agent-Alias-Token` (channels authored with `publicEndpoint()`).
|
|
77
|
-
* Only valid on repo-backed deploys.
|
|
78
75
|
*/
|
|
79
76
|
publicRoutePaths?: string[];
|
|
80
77
|
};
|
|
@@ -441,9 +438,7 @@ export class AgentServeDeployClient {
|
|
|
441
438
|
const body: Record<string, unknown> = {
|
|
442
439
|
teamId: args.teamId,
|
|
443
440
|
slug: args.slug,
|
|
444
|
-
|
|
445
|
-
? {}
|
|
446
|
-
: { gitRepoUrl: args.source.gitRepoUrl }),
|
|
441
|
+
gitRepoUrl: args.source.gitRepoUrl,
|
|
447
442
|
...(args.source.gitRef === undefined
|
|
448
443
|
? {}
|
|
449
444
|
: { gitRef: args.source.gitRef }),
|
|
@@ -113,7 +113,7 @@ export function agentPathRelativeToGitRoot(
|
|
|
113
113
|
|
|
114
114
|
/**
|
|
115
115
|
* Probe the git checkout containing `dir` for deploy source fields.
|
|
116
|
-
* Missing pieces stay undefined
|
|
116
|
+
* Missing pieces stay undefined.
|
|
117
117
|
*/
|
|
118
118
|
export async function inferDeployGitSource(
|
|
119
119
|
dir: string,
|
|
@@ -8,9 +8,8 @@
|
|
|
8
8
|
* An optional in-process sampler (`AGENT_SDK_MEMORY_METRICS=1`) still logs
|
|
9
9
|
* and, if a Datadog key happens to be in the pod, posts directly.
|
|
10
10
|
*
|
|
11
|
-
* `event_log.
|
|
12
|
-
*
|
|
13
|
-
* are equal today; a later consume-and-drop change should split them.
|
|
11
|
+
* Event-log residency is `event_log.resident_sessions` (open session
|
|
12
|
+
* map). Transcripts live on disk; unflushed tails are not a scrape gauge.
|
|
14
13
|
*/
|
|
15
14
|
|
|
16
15
|
import { getHeapStatistics } from "node:v8";
|
|
@@ -102,7 +101,7 @@ export function sampleProcessMemory(
|
|
|
102
101
|
|
|
103
102
|
export type MemoryHealthFields = {
|
|
104
103
|
memory: ProcessMemorySample & { heapUsedRatio: number };
|
|
105
|
-
eventLog: EventLogMemoryStats
|
|
104
|
+
eventLog: Pick<EventLogMemoryStats, "residentSessions">;
|
|
106
105
|
};
|
|
107
106
|
|
|
108
107
|
export function heapUsedRatio(memory: ProcessMemorySample): number {
|
|
@@ -132,7 +131,7 @@ export function buildMemoryHealthFields(
|
|
|
132
131
|
): MemoryHealthFields {
|
|
133
132
|
return {
|
|
134
133
|
memory: { ...sample, heapUsedRatio: heapUsedRatio(sample) },
|
|
135
|
-
eventLog,
|
|
134
|
+
eventLog: { residentSessions: eventLog.residentSessions },
|
|
136
135
|
};
|
|
137
136
|
}
|
|
138
137
|
|
|
@@ -167,9 +166,7 @@ export function formatMemoryLogLine(args: {
|
|
|
167
166
|
` heap_limit_mb=${mb(args.memory.heapLimitBytes)}` +
|
|
168
167
|
` heap_used_ratio=${ratio.toFixed(3)}` +
|
|
169
168
|
` rss_mb=${mb(args.memory.rssBytes)}` +
|
|
170
|
-
` event_log.sessions=${args.eventLog.residentSessions}`
|
|
171
|
-
` event_log.retained=${args.eventLog.eventsRetained}` +
|
|
172
|
-
` event_log.total=${args.eventLog.eventsTotal}`
|
|
169
|
+
` event_log.sessions=${args.eventLog.residentSessions}`
|
|
173
170
|
);
|
|
174
171
|
}
|
|
175
172
|
|
|
@@ -209,14 +206,6 @@ export function buildDatadogGaugeSeries(args: {
|
|
|
209
206
|
name: `${EVENT_LOG_METRIC_PREFIX}.resident_sessions`,
|
|
210
207
|
value: args.eventLog.residentSessions,
|
|
211
208
|
},
|
|
212
|
-
{
|
|
213
|
-
name: `${EVENT_LOG_METRIC_PREFIX}.events_retained`,
|
|
214
|
-
value: args.eventLog.eventsRetained,
|
|
215
|
-
},
|
|
216
|
-
{
|
|
217
|
-
name: `${EVENT_LOG_METRIC_PREFIX}.events_total`,
|
|
218
|
-
value: args.eventLog.eventsTotal,
|
|
219
|
-
},
|
|
220
209
|
];
|
|
221
210
|
return points.map((point) => ({
|
|
222
211
|
metric: point.name,
|
|
@@ -300,14 +289,6 @@ function recordOtel(
|
|
|
300
289
|
memory.rssBytes,
|
|
301
290
|
attributes
|
|
302
291
|
);
|
|
303
|
-
histogram(meter, `${EVENT_LOG_METRIC_PREFIX}.events_retained`).record(
|
|
304
|
-
eventLog.eventsRetained,
|
|
305
|
-
attributes
|
|
306
|
-
);
|
|
307
|
-
histogram(meter, `${EVENT_LOG_METRIC_PREFIX}.events_total`).record(
|
|
308
|
-
eventLog.eventsTotal,
|
|
309
|
-
attributes
|
|
310
|
-
);
|
|
311
292
|
histogram(meter, `${EVENT_LOG_METRIC_PREFIX}.resident_sessions`).record(
|
|
312
293
|
eventLog.residentSessions,
|
|
313
294
|
attributes
|