@cursor/july 0.1.49 → 0.1.51
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 +3 -3
- package/README.md +7 -14
- package/dist/bin/agent-serve.js +22 -8
- package/dist/channels/slack/init.d.ts +2 -3
- package/dist/channels/slack/init.d.ts.map +1 -1
- package/dist/channels/slack/init.js +8 -43
- package/dist/channels/slack/manifest.d.ts +3 -3
- package/dist/channels/slack/manifest.js +3 -3
- package/dist/channels/slack/setup.d.ts +2 -11
- package/dist/channels/slack/setup.d.ts.map +1 -1
- package/dist/channels/slack/setup.js +26 -94
- package/dist/channels/slack/types.d.ts +3 -4
- package/dist/channels/slack/types.d.ts.map +1 -1
- package/dist/docs/404.html +2 -2
- package/dist/docs/ab.html +3 -3
- package/dist/docs/assets/{app.BpWAgjaR.js → app.C51TX1Bw.js} +1 -1
- package/dist/docs/assets/chunks/@localSearchIndexroot.D2DpXdZN.js +1 -0
- package/dist/docs/assets/chunks/{VPLocalSearchBox.CnAktZtG.js → VPLocalSearchBox.BB9HCJPq.js} +1 -1
- package/dist/docs/assets/chunks/{theme.B5IvUxOB.js → theme.Dp-0SZcu.js} +2 -2
- package/dist/docs/assets/{deployment.md.Cdp4rTE4.js → deployment.md.B8kW-h7P.js} +1 -1
- package/dist/docs/assets/{example-agents_benny.md.l7JTmm8X.js → example-agents_benny.md.B-LIDGja.js} +1 -1
- package/dist/docs/assets/{example-agents_oncall.md.ZE0n6ZFN.js → example-agents_oncall.md.CBmyuAKc.js} +2 -2
- package/dist/docs/assets/{guides_human-in-the-loop.md.BWvT7UqY.js → guides_human-in-the-loop.md.Cf8kIIqC.js} +2 -2
- package/dist/docs/assets/{guides_human-in-the-loop.md.BWvT7UqY.lean.js → guides_human-in-the-loop.md.Cf8kIIqC.lean.js} +1 -1
- package/dist/docs/assets/{guides_slack.md.zriQpU_9.js → guides_slack.md.D2nTRu9M.js} +7 -12
- package/dist/docs/assets/guides_slack.md.D2nTRu9M.lean.js +1 -0
- package/dist/docs/assets/{reference_cli.md.BDmjPPAm.js → reference_cli.md.CGKfz_Yj.js} +3 -7
- package/dist/docs/assets/{reference_cli.md.BDmjPPAm.lean.js → reference_cli.md.CGKfz_Yj.lean.js} +1 -1
- package/dist/docs/assets/{troubleshooting.md.CkgwmVI0.js → troubleshooting.md.C6n3wCr6.js} +1 -1
- package/dist/docs/building-with-agents.html +3 -3
- package/dist/docs/concepts.html +3 -3
- package/dist/docs/deployment.html +5 -5
- package/dist/docs/evals.html +3 -3
- package/dist/docs/example-agents/approval-buddy.html +3 -3
- package/dist/docs/example-agents/benny.html +5 -5
- package/dist/docs/example-agents/bugbot.html +3 -3
- package/dist/docs/example-agents/codebase-wiki.html +3 -3
- package/dist/docs/example-agents/codeowners-review.html +3 -3
- package/dist/docs/example-agents/concierge.html +3 -3
- package/dist/docs/example-agents/fsd.html +3 -3
- package/dist/docs/example-agents/index.html +3 -3
- package/dist/docs/example-agents/knowledge-base.html +3 -3
- package/dist/docs/example-agents/oncall.html +6 -6
- package/dist/docs/example-agents/security-reviewer.html +3 -3
- package/dist/docs/example-agents/slack-agent.html +3 -3
- package/dist/docs/example-agents/weather-agent.html +3 -3
- package/dist/docs/guides/agent-to-agent.html +3 -3
- package/dist/docs/guides/cloud-runtime.html +3 -3
- package/dist/docs/guides/github.html +3 -3
- package/dist/docs/guides/human-in-the-loop.html +5 -5
- package/dist/docs/guides/mcp-oauth.html +3 -3
- package/dist/docs/guides/opentelemetry.html +3 -3
- package/dist/docs/guides/slack.html +11 -16
- package/dist/docs/guides/webhooks.html +3 -3
- package/dist/docs/hashmap.json +1 -1
- package/dist/docs/hillclimbing.html +3 -3
- package/dist/docs/index.html +3 -3
- package/dist/docs/quickstart.html +3 -3
- package/dist/docs/reference/agent-config.html +3 -3
- package/dist/docs/reference/artifacts.html +3 -3
- package/dist/docs/reference/channels.html +3 -3
- package/dist/docs/reference/cli.html +6 -10
- package/dist/docs/reference/connections.html +3 -3
- package/dist/docs/reference/hooks.html +3 -3
- package/dist/docs/reference/http-api.html +3 -3
- package/dist/docs/reference/instructions.html +3 -3
- package/dist/docs/reference/playground.html +3 -3
- package/dist/docs/reference/project-layout.html +3 -3
- package/dist/docs/reference/prompt.html +3 -3
- package/dist/docs/reference/schedules.html +3 -3
- package/dist/docs/reference/sessions.html +3 -3
- package/dist/docs/reference/skills.html +3 -3
- package/dist/docs/reference/subagents.html +3 -3
- package/dist/docs/reference/tools.html +3 -3
- package/dist/docs/scaffolding-agents.html +3 -3
- package/dist/docs/storage.html +3 -3
- package/dist/docs/templates/triage.html +3 -3
- package/dist/docs/troubleshooting.html +5 -5
- package/dist/internal/cli-ax.js +3 -3
- package/dist/internal/cli-cursor.d.ts +18 -0
- package/dist/internal/cli-cursor.d.ts.map +1 -1
- package/dist/internal/cli-cursor.js +25 -0
- package/dist/internal/cli-slack.d.ts +3 -4
- package/dist/internal/cli-slack.d.ts.map +1 -1
- package/dist/internal/cli-slack.js +126 -322
- package/dist/internal/convert-automation/convert-workflow.js +2 -2
- package/dist/internal/slack-provision-client.d.ts +37 -2
- package/dist/internal/slack-provision-client.d.ts.map +1 -1
- package/dist/internal/slack-provision-client.js +46 -0
- package/dist/playground/assets/{index-Dq_HqyU-.js → index-BKMz70wx.js} +39 -39
- package/dist/playground/assets/index-BmMrulSc.css +1 -0
- package/dist/playground/index.html +2 -2
- package/docs/deployment.md +1 -1
- package/docs/example-agents/benny.md +2 -3
- package/docs/example-agents/oncall.md +4 -5
- package/docs/guides/human-in-the-loop.md +1 -1
- package/docs/guides/slack.md +51 -112
- package/docs/reference/cli.md +14 -37
- package/docs/troubleshooting.md +2 -2
- package/package.json +1 -1
- package/skills/create-agent/SKILL.md +4 -5
- package/skills/setup-slack/SKILL.md +73 -131
- package/src/bin/agent-serve.ts +26 -9
- package/src/channels/slack/init.ts +10 -71
- package/src/channels/slack/manifest.ts +3 -3
- package/src/channels/slack/setup.ts +25 -103
- package/src/channels/slack/types.ts +3 -4
- package/src/internal/cli-ax.ts +3 -3
- package/src/internal/cli-cursor.ts +34 -0
- package/src/internal/cli-slack.ts +136 -389
- package/src/internal/convert-automation/convert-workflow.ts +2 -2
- package/src/internal/slack-provision-client.ts +83 -2
- package/dist/docs/assets/chunks/@localSearchIndexroot.D00d772K.js +0 -1
- package/dist/docs/assets/guides_slack.md.zriQpU_9.lean.js +0 -1
- package/dist/playground/assets/index-Dox74yWJ.css +0 -1
- /package/dist/docs/assets/{deployment.md.Cdp4rTE4.lean.js → deployment.md.B8kW-h7P.lean.js} +0 -0
- /package/dist/docs/assets/{example-agents_benny.md.l7JTmm8X.lean.js → example-agents_benny.md.B-LIDGja.lean.js} +0 -0
- /package/dist/docs/assets/{example-agents_oncall.md.ZE0n6ZFN.lean.js → example-agents_oncall.md.CBmyuAKc.lean.js} +0 -0
- /package/dist/docs/assets/{troubleshooting.md.CkgwmVI0.lean.js → troubleshooting.md.C6n3wCr6.lean.js} +0 -0
|
@@ -1,7 +1,6 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* `agent-serve slack …` CLI —
|
|
3
|
-
*
|
|
4
|
-
* "Add to Slack" API (`slack-provision-client.ts`).
|
|
2
|
+
* `agent-serve slack …` CLI — doctor, destroy/icon, and `create` as a
|
|
3
|
+
* dashboard-wizard launcher (`slack-provision-client.ts`).
|
|
5
4
|
*/
|
|
6
5
|
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
|
|
7
6
|
function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
|
|
@@ -14,10 +13,11 @@ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, ge
|
|
|
14
13
|
};
|
|
15
14
|
import { access, chmod, mkdir, readFile, writeFile } from "node:fs/promises";
|
|
16
15
|
import { basename, extname, join, resolve as resolvePath } from "node:path";
|
|
17
|
-
import {
|
|
16
|
+
import { buildSlackManifestPair, envPrefixFromName, formatSlackSetupGuide, initSlackChannel, runSlackDoctor, slackEnvKeys, } from "../channels/slack/index.js";
|
|
18
17
|
import { resolveDeployAuth } from "./cli-deploy.js";
|
|
19
18
|
import { cursorExternalApiUrl } from "./cursor/credentials.js";
|
|
20
19
|
import { slugFromDirectoryName } from "./deploy-client.js";
|
|
20
|
+
import { inferDeployGitSource } from "./deploy-source.js";
|
|
21
21
|
import { describeError } from "./describe-error.js";
|
|
22
22
|
import { CLI_COMMAND_NAME as CLI } from "./distribution.js";
|
|
23
23
|
import { loadAgentLocalEnv } from "./local-env.js";
|
|
@@ -34,33 +34,26 @@ Usage:
|
|
|
34
34
|
[--team <id>] [--json]
|
|
35
35
|
${CLI} slack icon <URL-or-FILE> [--dir .] [--slack-team T0123ABCD]
|
|
36
36
|
[--prod] [--team <id>] [--json]
|
|
37
|
-
${CLI} slack init [--dir .] [--name NAME] [--prefix PREFIX | --no-prefix]
|
|
38
|
-
[--channel-posts]
|
|
39
|
-
${CLI} slack manifest --env dev|prod|both [--dir .] [--name NAME]
|
|
40
|
-
[--print]
|
|
41
37
|
${CLI} slack doctor [--dir .] [--prefix PREFIX | --no-prefix] [--json]
|
|
42
38
|
|
|
43
|
-
create/destroy/icon —
|
|
44
|
-
create
|
|
45
|
-
consent
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
\`${CLI} login\` (or CURSOR_API_KEY) and the feature gate.
|
|
50
|
-
--prod provisions the prod app (default dev); --slack-team picks
|
|
51
|
-
the workspace when several are connected. --icon sets the app's
|
|
52
|
-
icon (best-effort: a bad icon warns, never fails the create).
|
|
53
|
-
A re-run refuses to overwrite manifest edits made by hand at
|
|
54
|
-
api.slack.com (manifest_drift); --force overwrites them.
|
|
39
|
+
create/destroy/icon — dedicated Socket Mode app via the Cursor dashboard:
|
|
40
|
+
create Opens the signed-in dashboard wizard. Finish name, workspace,
|
|
41
|
+
and Slack consent there. The CLI writes the token pair into
|
|
42
|
+
<dir>/.env.local and runs doctor. Requires \`${CLI} login\`
|
|
43
|
+
(or CURSOR_API_KEY). --prod provisions the prod app (default
|
|
44
|
+
dev). --name / --icon / --channel-posts prefill the wizard.
|
|
55
45
|
destroy Delete the provisioned Slack app for this agent (+env). Tokens
|
|
56
46
|
already in .env.local are left in place (they just stop working).
|
|
57
47
|
icon Set the provisioned app's icon: an https image URL or a local
|
|
58
48
|
png/jpg/gif file (at most 512KB).
|
|
59
49
|
|
|
50
|
+
\`slack init\` is removed. Use \`slack create\`. Do not paste a manifest
|
|
51
|
+
at api.slack.com.
|
|
52
|
+
|
|
60
53
|
Env tokens:
|
|
61
54
|
single agent: SLACK_BOT_TOKEN + SLACK_APP_TOKEN
|
|
62
55
|
multi-agent: <PREFIX>_SLACK_BOT_TOKEN + <PREFIX>_SLACK_APP_TOKEN
|
|
63
|
-
(
|
|
56
|
+
(create default PREFIX from the directory name;
|
|
64
57
|
doctor defaults to it too when those keys are set)
|
|
65
58
|
|
|
66
59
|
See also: skills/setup-slack/SKILL.md
|
|
@@ -98,37 +91,13 @@ export function cmdSlack(subcommand, options) {
|
|
|
98
91
|
}
|
|
99
92
|
function cmdSlackInit(options) {
|
|
100
93
|
return __awaiter(this, void 0, void 0, function* () {
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
: { envPrefix: options.prefix })), (options.channelPosts === true ? { channelPosts: true } : {})));
|
|
109
|
-
process.stdout.write(`${JSON.stringify({
|
|
110
|
-
ok: true,
|
|
111
|
-
directory: result.rootDir,
|
|
112
|
-
created: result.files,
|
|
113
|
-
envPrefix: (_a = result.envPrefix) !== null && _a !== void 0 ? _a : null,
|
|
114
|
-
envKeys: result.envKeys,
|
|
115
|
-
next: [
|
|
116
|
-
"Open https://api.slack.com/apps → Create from manifest",
|
|
117
|
-
"Import .agent-serve/slack/manifest.dev.json (then prod)",
|
|
118
|
-
`Install apps; set ${result.envKeys.bot} + ${result.envKeys.app}`,
|
|
119
|
-
result.envPrefix === undefined
|
|
120
|
-
? "agent-serve slack doctor"
|
|
121
|
-
: `agent-serve slack doctor --prefix ${result.envPrefix}`,
|
|
122
|
-
"agent-serve serve --dir . --dev",
|
|
123
|
-
"agent-serve slack setup",
|
|
124
|
-
],
|
|
125
|
-
}, null, 2)}\n`);
|
|
126
|
-
return 0;
|
|
127
|
-
}
|
|
128
|
-
catch (error) {
|
|
129
|
-
process.stderr.write(`${error instanceof Error ? error.message : String(error)}\n`);
|
|
130
|
-
return 1;
|
|
131
|
-
}
|
|
94
|
+
errStream(options)(`\`slack init\` is removed. Socket Mode apps are created with:
|
|
95
|
+
|
|
96
|
+
${CLI} slack create --dir .
|
|
97
|
+
|
|
98
|
+
That opens the Cursor dashboard wizard. Do not paste a manifest at api.slack.com.
|
|
99
|
+
`);
|
|
100
|
+
return 1;
|
|
132
101
|
});
|
|
133
102
|
}
|
|
134
103
|
function runManifest(options) {
|
|
@@ -137,10 +106,11 @@ function runManifest(options) {
|
|
|
137
106
|
const env = (_a = options.env) !== null && _a !== void 0 ? _a : "both";
|
|
138
107
|
const dir = resolvePath(options.dir);
|
|
139
108
|
const name = (_b = options.name) !== null && _b !== void 0 ? _b : basename(dir);
|
|
140
|
-
const pair = buildSlackManifestPair(
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
109
|
+
const pair = buildSlackManifestPair({
|
|
110
|
+
name,
|
|
111
|
+
managedByCursor: false,
|
|
112
|
+
channelPosts: options.channelPosts === true ? true : undefined,
|
|
113
|
+
});
|
|
144
114
|
if (options.print) {
|
|
145
115
|
if (env === "dev" || env === "both") {
|
|
146
116
|
process.stdout.write(`${JSON.stringify(pair.dev, null, 2)}\n`);
|
|
@@ -210,8 +180,8 @@ function resolveDoctorEnvPrefix(options, dir) {
|
|
|
210
180
|
// ============================================================================
|
|
211
181
|
// create / destroy — Cursor "Add to Slack" managed provisioning
|
|
212
182
|
// ============================================================================
|
|
213
|
-
const
|
|
214
|
-
const
|
|
183
|
+
const DEFAULT_WIZARD_POLL_INTERVAL_MS = 3000;
|
|
184
|
+
const DEFAULT_WIZARD_POLL_TIMEOUT_MS = 60 * 60 * 1000;
|
|
215
185
|
function resolveSlackAppTarget(options, err) {
|
|
216
186
|
var _a, _b;
|
|
217
187
|
if (options.env === "both" && options.prod !== true) {
|
|
@@ -285,38 +255,26 @@ function toSlackAppIconInput(source) {
|
|
|
285
255
|
: { base64: source.base64, contentType: source.contentType };
|
|
286
256
|
}
|
|
287
257
|
/**
|
|
288
|
-
*
|
|
289
|
-
*
|
|
258
|
+
* File icons post after the wizard finishes. URL icons ride the mint
|
|
259
|
+
* payload. Best-effort: failures warn, never fail the create.
|
|
290
260
|
*/
|
|
291
|
-
function
|
|
261
|
+
function applySlackCreateFileIcon(ctx, source, appId) {
|
|
292
262
|
return __awaiter(this, void 0, void 0, function* () {
|
|
293
|
-
if (source === undefined) {
|
|
263
|
+
if (source === undefined || source.kind === "url") {
|
|
294
264
|
return undefined;
|
|
295
265
|
}
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
else {
|
|
303
|
-
try {
|
|
304
|
-
yield ctx.client.setAppIcon({
|
|
305
|
-
teamId: ctx.teamId,
|
|
306
|
-
slackAppId: result.appId,
|
|
307
|
-
icon: toSlackAppIconInput(source),
|
|
308
|
-
});
|
|
309
|
-
}
|
|
310
|
-
catch (error) {
|
|
311
|
-
failure = describeError(error);
|
|
312
|
-
}
|
|
313
|
-
}
|
|
314
|
-
if (failure === undefined) {
|
|
266
|
+
try {
|
|
267
|
+
yield ctx.client.setAppIcon({
|
|
268
|
+
teamId: ctx.teamId,
|
|
269
|
+
slackAppId: appId,
|
|
270
|
+
icon: toSlackAppIconInput(source),
|
|
271
|
+
});
|
|
315
272
|
return "set";
|
|
316
273
|
}
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
|
|
274
|
+
catch (error) {
|
|
275
|
+
ctx.err(`warning: setting the Slack app icon failed: ${describeError(error)}\nRetry with \`${CLI} slack icon ${source.path}\`.\n`);
|
|
276
|
+
return "failed";
|
|
277
|
+
}
|
|
320
278
|
});
|
|
321
279
|
}
|
|
322
280
|
/**
|
|
@@ -351,102 +309,18 @@ function prepareProvision(options) {
|
|
|
351
309
|
out,
|
|
352
310
|
err,
|
|
353
311
|
openBrowserImpl: (_b = options.openBrowserImpl) !== null && _b !== void 0 ? _b : openBrowser,
|
|
354
|
-
pollIntervalMs: (_c = options.connectPollIntervalMs) !== null && _c !== void 0 ? _c :
|
|
355
|
-
pollTimeoutMs: (_d = options.connectPollTimeoutMs) !== null && _d !== void 0 ? _d :
|
|
312
|
+
pollIntervalMs: (_c = options.connectPollIntervalMs) !== null && _c !== void 0 ? _c : DEFAULT_WIZARD_POLL_INTERVAL_MS,
|
|
313
|
+
pollTimeoutMs: (_d = options.connectPollTimeoutMs) !== null && _d !== void 0 ? _d : DEFAULT_WIZARD_POLL_TIMEOUT_MS,
|
|
356
314
|
};
|
|
357
315
|
});
|
|
358
316
|
}
|
|
359
|
-
/**
|
|
360
|
-
* Slack's team-scoped admin page listing pending app-install requests.
|
|
361
|
-
* Provisioned apps have no OAuth flow (no redirect URLs, install-from-Slack
|
|
362
|
-
* disabled), so the oauth_authorize_url Slack returns from
|
|
363
|
-
* apps.manifest.create dead-ends; approval happens here instead.
|
|
364
|
-
*/
|
|
365
|
-
function slackAppRequestsUrl(slackTeamId) {
|
|
366
|
-
return `https://app.slack.com/apps-manage/${encodeURIComponent(slackTeamId)}/integrations/requests`;
|
|
367
|
-
}
|
|
368
|
-
/** "Name (T0123ABCD)", or the bare id when the backend has no name. */
|
|
369
|
-
function formatWorkspace(workspace) {
|
|
370
|
-
var _a;
|
|
371
|
-
const name = (_a = workspace.slackTeamName) !== null && _a !== void 0 ? _a : "";
|
|
372
|
-
return name === ""
|
|
373
|
-
? workspace.slackTeamId
|
|
374
|
-
: `${name} (${workspace.slackTeamId})`;
|
|
375
|
-
}
|
|
376
|
-
function matchConnectedWorkspace(workspaces, wait) {
|
|
377
|
-
const candidates = wait.slackTeamId === undefined
|
|
378
|
-
? workspaces
|
|
379
|
-
: workspaces.filter((workspace) => workspace.slackTeamId === wait.slackTeamId);
|
|
380
|
-
if (wait.notConnectedAt === undefined) {
|
|
381
|
-
return candidates[0];
|
|
382
|
-
}
|
|
383
|
-
return candidates.find((workspace) => workspace.connectedAt !== wait.notConnectedAt);
|
|
384
|
-
}
|
|
385
|
-
/**
|
|
386
|
-
* Mint the browser consent URL, open it, and poll `GET /status` until the
|
|
387
|
-
* awaited workspace shows up (or the poll times out). Returns the
|
|
388
|
-
* connected workspace, or an exit code with the error already rendered.
|
|
389
|
-
*/
|
|
390
|
-
function runConnectFlow(ctx, wait) {
|
|
391
|
-
return __awaiter(this, void 0, void 0, function* () {
|
|
392
|
-
let url;
|
|
393
|
-
try {
|
|
394
|
-
url = yield ctx.client.connect(ctx.teamId);
|
|
395
|
-
}
|
|
396
|
-
catch (error) {
|
|
397
|
-
return renderSlackApiError(error, ctx.err);
|
|
398
|
-
}
|
|
399
|
-
ctx.err(`\nConnect your Slack workspace to Cursor — approve in the browser:\n\n ${url}\n\n` +
|
|
400
|
-
`After approving in Slack you land on cursor.com, which links the workspace to your Cursor account — sign in there if the browser is not already signed in as you.\n\n` +
|
|
401
|
-
`(waiting for the Slack consent to complete, up to ${Math.round(ctx.pollTimeoutMs / 60000)} minutes)\n`);
|
|
402
|
-
ctx.openBrowserImpl(url);
|
|
403
|
-
const deadline = Date.now() + ctx.pollTimeoutMs;
|
|
404
|
-
let lastPollFailure;
|
|
405
|
-
for (;;) {
|
|
406
|
-
let workspace;
|
|
407
|
-
let sleepMs = ctx.pollIntervalMs;
|
|
408
|
-
try {
|
|
409
|
-
const status = yield ctx.client.status(ctx.teamId);
|
|
410
|
-
workspace = matchConnectedWorkspace(status.workspaces, wait);
|
|
411
|
-
lastPollFailure = undefined;
|
|
412
|
-
}
|
|
413
|
-
catch (error) {
|
|
414
|
-
// Transient poll failures must not abort a minted consent flow; only
|
|
415
|
-
// a still-failing poll at the deadline surfaces the error.
|
|
416
|
-
if (Date.now() >= deadline) {
|
|
417
|
-
return renderSlackApiError(error, ctx.err);
|
|
418
|
-
}
|
|
419
|
-
const failure = describeError(error);
|
|
420
|
-
if (failure !== lastPollFailure) {
|
|
421
|
-
lastPollFailure = failure;
|
|
422
|
-
ctx.err(`(status poll failed, retrying: ${failure})\n`);
|
|
423
|
-
}
|
|
424
|
-
if (error instanceof SlackProvisionApiError &&
|
|
425
|
-
error.retryAfterSeconds !== undefined) {
|
|
426
|
-
// Respect backend backpressure instead of hammering at the poll rate.
|
|
427
|
-
sleepMs = Math.max(sleepMs, error.retryAfterSeconds * 1000);
|
|
428
|
-
}
|
|
429
|
-
}
|
|
430
|
-
if (workspace !== undefined) {
|
|
431
|
-
ctx.err(`Connected Slack workspace ${workspace.slackTeamId}.\n`);
|
|
432
|
-
return workspace;
|
|
433
|
-
}
|
|
434
|
-
if (Date.now() >= deadline) {
|
|
435
|
-
ctx.err(`Timed out waiting for the Slack workspace connection. The consent link stays valid for ~10 minutes — approve it, finish the sign-in on cursor.com, then re-run \`${CLI} slack create\`.\n`);
|
|
436
|
-
return 1;
|
|
437
|
-
}
|
|
438
|
-
yield sleep(sleepMs);
|
|
439
|
-
}
|
|
440
|
-
});
|
|
441
|
-
}
|
|
442
317
|
export function cmdSlackCreate(options) {
|
|
443
318
|
return __awaiter(this, void 0, void 0, function* () {
|
|
444
|
-
var _a;
|
|
319
|
+
var _a, _b, _c;
|
|
445
320
|
const target = resolveSlackAppTarget(options, errStream(options));
|
|
446
321
|
if (typeof target === "number") {
|
|
447
322
|
return target;
|
|
448
323
|
}
|
|
449
|
-
// Resolved before any network I/O so a bad path/type/size fails fast.
|
|
450
324
|
let iconSource;
|
|
451
325
|
if (options.icon !== undefined) {
|
|
452
326
|
const resolved = yield resolveSlackIconSource(options.icon, errStream(options));
|
|
@@ -459,38 +333,6 @@ export function cmdSlackCreate(options) {
|
|
|
459
333
|
if (typeof ctx === "number") {
|
|
460
334
|
return ctx;
|
|
461
335
|
}
|
|
462
|
-
// Workspace: --slack-team wins; one → use it; none → connect; several → ask.
|
|
463
|
-
let status;
|
|
464
|
-
try {
|
|
465
|
-
status = yield ctx.client.status(ctx.teamId);
|
|
466
|
-
}
|
|
467
|
-
catch (error) {
|
|
468
|
-
return renderSlackApiError(error, ctx.err);
|
|
469
|
-
}
|
|
470
|
-
let workspace;
|
|
471
|
-
if (options.slackTeam !== undefined) {
|
|
472
|
-
workspace = status.workspaces.find((candidate) => candidate.slackTeamId === options.slackTeam);
|
|
473
|
-
}
|
|
474
|
-
else if (status.workspaces.length === 1) {
|
|
475
|
-
workspace = status.workspaces[0];
|
|
476
|
-
}
|
|
477
|
-
else if (status.workspaces.length > 1) {
|
|
478
|
-
ctx.err(`Multiple Slack workspaces are connected:\n` +
|
|
479
|
-
status.workspaces
|
|
480
|
-
.map((candidate) => ` - ${formatWorkspace(candidate)} (Slack user ${candidate.slackUserId}, connected ${candidate.connectedAt})\n`)
|
|
481
|
-
.join("") +
|
|
482
|
-
`Pass --slack-team <id> to pick one.\n`);
|
|
483
|
-
return 1;
|
|
484
|
-
}
|
|
485
|
-
if (workspace === undefined) {
|
|
486
|
-
const connected = yield runConnectFlow(ctx, {
|
|
487
|
-
slackTeamId: options.slackTeam,
|
|
488
|
-
});
|
|
489
|
-
if (typeof connected === "number") {
|
|
490
|
-
return connected;
|
|
491
|
-
}
|
|
492
|
-
workspace = connected;
|
|
493
|
-
}
|
|
494
336
|
let wrote;
|
|
495
337
|
try {
|
|
496
338
|
wrote = yield scaffoldSlackChannelFiles(options, target);
|
|
@@ -502,90 +344,80 @@ export function cmdSlackCreate(options) {
|
|
|
502
344
|
if (wrote.length > 0 && !ctx.json) {
|
|
503
345
|
ctx.err(`Scaffolded the Slack channel: ${wrote.join(", ")}\n`);
|
|
504
346
|
}
|
|
505
|
-
|
|
506
|
-
|
|
507
|
-
|
|
508
|
-
|
|
509
|
-
|
|
510
|
-
|
|
511
|
-
|
|
512
|
-
|
|
513
|
-
|
|
514
|
-
|
|
515
|
-
|
|
516
|
-
|
|
347
|
+
const source = yield inferDeployGitSource(target.dir);
|
|
348
|
+
let minted;
|
|
349
|
+
try {
|
|
350
|
+
minted = yield ctx.client.mintWizard({
|
|
351
|
+
teamId: ctx.teamId,
|
|
352
|
+
slug: target.slug,
|
|
353
|
+
env: target.env,
|
|
354
|
+
envPrefix: target.envPrefix,
|
|
355
|
+
name: (_a = options.name) !== null && _a !== void 0 ? _a : target.name,
|
|
356
|
+
gitRepoUrl: source.gitRepoUrl,
|
|
357
|
+
gitRef: source.gitRef,
|
|
358
|
+
agentPath: source.agentPath,
|
|
359
|
+
channelPosts: options.channelPosts === true ? true : undefined,
|
|
360
|
+
slackTeamId: options.slackTeam,
|
|
361
|
+
iconUrl: (iconSource === null || iconSource === void 0 ? void 0 : iconSource.kind) === "url" ? iconSource.url : undefined,
|
|
362
|
+
force: options.force === true ? true : undefined,
|
|
363
|
+
});
|
|
364
|
+
}
|
|
365
|
+
catch (error) {
|
|
366
|
+
return renderSlackApiError(error, ctx.err);
|
|
367
|
+
}
|
|
368
|
+
ctx.err(`Finish Slack setup in the browser:\n\n ${minted.url}\n\n` +
|
|
369
|
+
`Sign in as the same Cursor account that ran this command. ` +
|
|
370
|
+
`The CLI waits here (up to ${Math.round(ctx.pollTimeoutMs / 60000)} minutes).\n`);
|
|
371
|
+
ctx.openBrowserImpl(minted.url);
|
|
372
|
+
const deadline = Date.now() + ctx.pollTimeoutMs;
|
|
373
|
+
let sawPendingAdmin = false;
|
|
374
|
+
let poll;
|
|
517
375
|
for (;;) {
|
|
518
376
|
try {
|
|
519
|
-
|
|
520
|
-
teamId: ctx.teamId,
|
|
521
|
-
slackTeamId: workspace.slackTeamId,
|
|
522
|
-
slug: target.slug,
|
|
523
|
-
env: target.env,
|
|
524
|
-
manifest: manifestJson,
|
|
525
|
-
icon: (iconSource === null || iconSource === void 0 ? void 0 : iconSource.kind) === "url" ? { url: iconSource.url } : undefined,
|
|
526
|
-
force: options.force === true ? true : undefined,
|
|
527
|
-
});
|
|
528
|
-
break;
|
|
377
|
+
poll = yield ctx.client.pollWizard(minted.wizardId);
|
|
529
378
|
}
|
|
530
379
|
catch (error) {
|
|
531
|
-
if (
|
|
532
|
-
(error.
|
|
533
|
-
error.code === "insufficient_scopes" ||
|
|
534
|
-
error.code === "manager_reauth_required") &&
|
|
535
|
-
!connectRetried) {
|
|
536
|
-
connectRetried = true;
|
|
537
|
-
// All three codes arrive holding a row that GET /status already
|
|
538
|
-
// lists, so the poll waits for a fresh grant instead of re-matching
|
|
539
|
-
// that row.
|
|
540
|
-
const wait = {
|
|
541
|
-
slackTeamId: workspace.slackTeamId,
|
|
542
|
-
notConnectedAt: workspace.connectedAt,
|
|
543
|
-
};
|
|
544
|
-
if (error.code === "insufficient_scopes") {
|
|
545
|
-
ctx.err(`The Slack connection is missing scopes provisioning needs; approving the consent link again grants them.\n`);
|
|
546
|
-
}
|
|
547
|
-
else if (error.code === "manager_reauth_required") {
|
|
548
|
-
ctx.err(`Slack revoked Cursor's authorization for workspace ${workspace.slackTeamId}; re-approving the consent link restores it.\n`);
|
|
549
|
-
}
|
|
550
|
-
else {
|
|
551
|
-
ctx.err(`The Slack workspace is not connected to Cursor; starting the connect flow.\n`);
|
|
552
|
-
}
|
|
553
|
-
const connected = yield runConnectFlow(ctx, wait);
|
|
554
|
-
if (typeof connected === "number") {
|
|
555
|
-
return connected;
|
|
556
|
-
}
|
|
557
|
-
workspace = connected;
|
|
558
|
-
continue;
|
|
380
|
+
if (Date.now() >= deadline) {
|
|
381
|
+
return renderSlackApiError(error, ctx.err);
|
|
559
382
|
}
|
|
560
|
-
|
|
383
|
+
yield sleep(ctx.pollIntervalMs);
|
|
384
|
+
continue;
|
|
561
385
|
}
|
|
562
|
-
|
|
563
|
-
|
|
564
|
-
|
|
565
|
-
|
|
566
|
-
|
|
567
|
-
|
|
568
|
-
ok: false,
|
|
569
|
-
appId: result.appId,
|
|
570
|
-
status: result.status,
|
|
571
|
-
slackTeamId: workspace.slackTeamId,
|
|
572
|
-
envKeys: target.envKeys,
|
|
573
|
-
wrote,
|
|
574
|
-
approveUrl,
|
|
575
|
-
iconStatus,
|
|
576
|
-
}, null, 2)}\n`);
|
|
386
|
+
if (poll.status === "complete") {
|
|
387
|
+
break;
|
|
388
|
+
}
|
|
389
|
+
if (poll.status === "failed") {
|
|
390
|
+
ctx.err(`${(_b = poll.error) !== null && _b !== void 0 ? _b : "Slack setup failed"}\n`);
|
|
391
|
+
return 1;
|
|
577
392
|
}
|
|
578
|
-
|
|
579
|
-
|
|
580
|
-
|
|
581
|
-
|
|
393
|
+
if (poll.status === "pending_admin_approval" && !sawPendingAdmin) {
|
|
394
|
+
sawPendingAdmin = true;
|
|
395
|
+
const appSuffix = poll.appId === undefined ? "" : ` (${poll.appId})`;
|
|
396
|
+
ctx.err(`Waiting for a Slack admin to approve the app${appSuffix}. Keep this command running.\n` +
|
|
397
|
+
`Managed install does not file the request. Open Slack to submit it; that same page lets you send a reminder to workspace owners.\n`);
|
|
398
|
+
if (poll.oauthAuthorizeUrl !== undefined &&
|
|
399
|
+
poll.oauthAuthorizeUrl.length > 0) {
|
|
400
|
+
ctx.err(` ${poll.oauthAuthorizeUrl}\n`);
|
|
401
|
+
}
|
|
402
|
+
ctx.err("After an admin approves, click Retry in the dashboard.\n");
|
|
403
|
+
}
|
|
404
|
+
if (Date.now() >= deadline) {
|
|
405
|
+
ctx.err(`Timed out waiting for Slack setup. Finish it in the browser, then re-run \`${CLI} slack create\`.\n ${minted.url}\n`);
|
|
406
|
+
return 1;
|
|
582
407
|
}
|
|
408
|
+
yield sleep(ctx.pollIntervalMs);
|
|
409
|
+
}
|
|
410
|
+
if (poll === undefined || poll.localEnv === undefined) {
|
|
411
|
+
ctx.err(poll.localEnvClaimed === true
|
|
412
|
+
? "Tokens were already claimed by another poll; re-run create to rotate them.\n"
|
|
413
|
+
: "Slack setup finished but no tokens were returned; re-run create.\n");
|
|
583
414
|
return 1;
|
|
584
415
|
}
|
|
585
|
-
|
|
586
|
-
|
|
587
|
-
|
|
588
|
-
|
|
416
|
+
const envEntries = [
|
|
417
|
+
{ key: target.envKeys.bot, value: poll.localEnv.botToken },
|
|
418
|
+
];
|
|
419
|
+
if (poll.localEnv.appToken !== undefined) {
|
|
420
|
+
envEntries.push({ key: target.envKeys.app, value: poll.localEnv.appToken });
|
|
589
421
|
}
|
|
590
422
|
const envFile = join(target.dir, ".env.local");
|
|
591
423
|
try {
|
|
@@ -602,29 +434,33 @@ export function cmdSlackCreate(options) {
|
|
|
602
434
|
for (const { key, value } of envEntries) {
|
|
603
435
|
process.env[key] = value;
|
|
604
436
|
}
|
|
605
|
-
if (
|
|
606
|
-
ctx.err(`warning: Slack did not return an app-level token, so ${target.envKeys.app} was not written
|
|
437
|
+
if (poll.localEnv.appToken === undefined) {
|
|
438
|
+
ctx.err(`warning: Slack did not return an app-level token, so ${target.envKeys.app} was not written; re-run \`${CLI} slack create\` to recover it.\n`);
|
|
439
|
+
}
|
|
440
|
+
let iconStatus;
|
|
441
|
+
if (poll.appId !== undefined) {
|
|
442
|
+
iconStatus = yield applySlackCreateFileIcon(ctx, iconSource, poll.appId);
|
|
607
443
|
}
|
|
608
444
|
if (ctx.json) {
|
|
609
445
|
ctx.out(`${JSON.stringify({
|
|
610
446
|
ok: true,
|
|
611
|
-
appId:
|
|
612
|
-
status:
|
|
613
|
-
slackTeamId: workspace.slackTeamId,
|
|
447
|
+
appId: poll.appId,
|
|
448
|
+
status: poll.status,
|
|
614
449
|
envKeys: target.envKeys,
|
|
615
450
|
wrote,
|
|
616
451
|
iconStatus,
|
|
617
452
|
}, null, 2)}\n`);
|
|
618
453
|
return 0;
|
|
619
454
|
}
|
|
620
|
-
ctx.out(`
|
|
621
|
-
|
|
622
|
-
|
|
455
|
+
ctx.out(`Slack is connected for ${target.slug}` +
|
|
456
|
+
(poll.appId === undefined ? "" : ` (app ${poll.appId})`) +
|
|
457
|
+
`.\n wrote ${envEntries.map((entry) => entry.key).join(" + ")} to ${envFile}\n\n`);
|
|
458
|
+
const doctor = (_c = options.doctorImpl) !== null && _c !== void 0 ? _c : runSlackDoctor;
|
|
623
459
|
const doctorResult = yield doctor(target.envPrefix === undefined ? {} : { envPrefix: target.envPrefix });
|
|
624
460
|
ctx.out(renderDoctorReport(doctorResult));
|
|
625
461
|
ctx.out(`\nNext steps:\n` +
|
|
626
|
-
` 1. ${CLI}
|
|
627
|
-
` 2.
|
|
462
|
+
` 1. ${CLI} serve --dir ${options.dir} --dev\n` +
|
|
463
|
+
` 2. Invite the bot in Slack and @mention it.\n`);
|
|
628
464
|
return 0;
|
|
629
465
|
});
|
|
630
466
|
}
|
|
@@ -758,10 +594,8 @@ function formatAppList(apps) {
|
|
|
758
594
|
.join("");
|
|
759
595
|
}
|
|
760
596
|
/**
|
|
761
|
-
* Scaffold
|
|
762
|
-
*
|
|
763
|
-
* EEXIST means "already scaffolded" (provisioning builds its manifest in
|
|
764
|
-
* memory and does not need the on-disk copies).
|
|
597
|
+
* Scaffold `agent/channels/slack.ts` when absent. Existing files are
|
|
598
|
+
* never overwritten: initSlackChannel writes with `wx`.
|
|
765
599
|
*/
|
|
766
600
|
function scaffoldSlackChannelFiles(options, target) {
|
|
767
601
|
return __awaiter(this, void 0, void 0, function* () {
|
|
@@ -877,42 +711,12 @@ function renderDoctorReport(result) {
|
|
|
877
711
|
text += result.ok ? "doctor: pass\n" : "doctor: fail\n";
|
|
878
712
|
return text;
|
|
879
713
|
}
|
|
880
|
-
/**
|
|
881
|
-
* Actionable rendering for `POST /apps` failures the create flow does not
|
|
882
|
-
* retry itself (the connect-and-retry codes are handled by the caller).
|
|
883
|
-
*/
|
|
884
|
-
function renderCreateProvisionError(error, err) {
|
|
885
|
-
var _a;
|
|
886
|
-
if (!(error instanceof SlackProvisionApiError)) {
|
|
887
|
-
throw error;
|
|
888
|
-
}
|
|
889
|
-
switch (error.code) {
|
|
890
|
-
case "app_conflict":
|
|
891
|
-
err(`${error.message}\nA concurrent \`${CLI} slack create\` for this agent won the race; re-run create to pick up that app.\n`);
|
|
892
|
-
return 1;
|
|
893
|
-
case "ratelimited":
|
|
894
|
-
err(`${error.message}\nRe-run \`${CLI} slack create\` after the wait.\n`);
|
|
895
|
-
return 1;
|
|
896
|
-
case "invalid_app_id":
|
|
897
|
-
err(`${error.message}\nThe provisioned Slack app seems to have been deleted outside Cursor. Run \`${CLI} slack destroy\` to drop the stale record, then re-run \`${CLI} slack create\`.\n`);
|
|
898
|
-
return 1;
|
|
899
|
-
case "manifest_drift":
|
|
900
|
-
err(`${error.message}\n` +
|
|
901
|
-
((_a = error.changedPaths) !== null && _a !== void 0 ? _a : []).map((path) => ` - ${path}\n`).join("") +
|
|
902
|
-
`Re-run \`${CLI} slack create --force\` to overwrite those edits.\n`);
|
|
903
|
-
return 1;
|
|
904
|
-
default:
|
|
905
|
-
return renderSlackApiError(error, err);
|
|
906
|
-
}
|
|
907
|
-
}
|
|
908
714
|
function renderSlackApiError(error, err) {
|
|
909
715
|
if (!(error instanceof SlackProvisionApiError)) {
|
|
910
716
|
throw error;
|
|
911
717
|
}
|
|
912
718
|
err(error.code === "manager_reauth_required"
|
|
913
|
-
?
|
|
914
|
-
// flow before provisioning.
|
|
915
|
-
`${error.message}\nRe-approve the Slack consent by re-running \`${CLI} slack create\`, which reconnects the workspace first.\n`
|
|
719
|
+
? `${error.message}\nRe-run \`${CLI} slack create\` and finish Slack consent in the dashboard.\n`
|
|
916
720
|
: `${error.message}\n`);
|
|
917
721
|
return 1;
|
|
918
722
|
}
|
|
@@ -681,7 +681,7 @@ function emitSlackChannel(automation, triggers, slugs, out) {
|
|
|
681
681
|
out.warnings.push("The Slack trigger marked triggering messages with a completion reaction; agentkit has no completion-reaction option, so that signal is lost unless agent/instructions.md tells the agent to add the reaction itself via Slack tools.");
|
|
682
682
|
}
|
|
683
683
|
if (socketMode) {
|
|
684
|
-
out.setupSteps.push(`Slack channel-watch / reaction / channel-created triggers need a dedicated Socket Mode Slack app (not the Cursor connection): run \`${CLI_COMMAND_NAME} slack
|
|
684
|
+
out.setupSteps.push(`Slack channel-watch / reaction / channel-created triggers need a dedicated Socket Mode Slack app (not the Cursor connection): run \`${CLI_COMMAND_NAME} slack create --channel-posts\`, then finish in the dashboard wizard.`);
|
|
685
685
|
}
|
|
686
686
|
const header = [
|
|
687
687
|
`import { slackChannel } from "${PACKAGE_NAME}/channels/slack";`,
|
|
@@ -727,7 +727,7 @@ function emitSlackChannel(automation, triggers, slugs, out) {
|
|
|
727
727
|
body = [
|
|
728
728
|
"//",
|
|
729
729
|
"// This channel needs a dedicated Socket Mode Slack app (not the Cursor",
|
|
730
|
-
`// connection): run \`${CLI_COMMAND_NAME} slack
|
|
730
|
+
`// connection): run \`${CLI_COMMAND_NAME} slack create --channel-posts\`.`,
|
|
731
731
|
"export default slackChannel({",
|
|
732
732
|
...engagement,
|
|
733
733
|
"});",
|