stageflow 0.30.0 → 0.32.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/dist/agent/cursorProvider.d.ts +8 -0
- package/dist/agent/cursorProvider.js +63 -13
- package/dist/agent/piAdapter.js +21 -0
- package/dist/agent/port.d.ts +2 -0
- package/dist/agent/providerAuth.d.ts +7 -3
- package/dist/agent/providerAuth.js +5 -3
- package/dist/cli/doctorCommand.js +7 -0
- package/dist/cli/providersCommand.d.ts +1 -1
- package/dist/cli/providersCommand.js +4 -4
- package/dist/cli.js +1 -1
- package/dist/config/loadStage.js +10 -0
- package/dist/config/pipelineStageKeys.js +1 -0
- package/dist/email/accounts.d.ts +109 -0
- package/dist/email/accounts.js +285 -0
- package/dist/email/adapter.d.ts +69 -0
- package/dist/email/adapter.js +720 -0
- package/dist/email/attachments.d.ts +21 -0
- package/dist/email/attachments.js +146 -0
- package/dist/email/events.d.ts +95 -0
- package/dist/email/events.js +292 -0
- package/dist/email/host.d.ts +15 -0
- package/dist/email/host.js +119 -0
- package/dist/email/messages.d.ts +44 -0
- package/dist/email/messages.js +182 -0
- package/dist/email/port.d.ts +161 -0
- package/dist/email/port.js +12 -0
- package/dist/email/replies.d.ts +5 -0
- package/dist/email/replies.js +56 -0
- package/dist/email/submissions.d.ts +31 -0
- package/dist/email/submissions.js +123 -0
- package/dist/email/triggers.d.ts +140 -0
- package/dist/email/triggers.js +563 -0
- package/dist/package-meta.d.ts +1 -1
- package/dist/package-meta.js +1 -1
- package/dist/runstore/diskUsage.d.ts +1 -1
- package/dist/runstore/diskUsage.js +2 -7
- package/dist/runstore/port.d.ts +4 -0
- package/dist/runstore/sqlite/SqliteRunStore.d.ts +1 -0
- package/dist/runstore/sqlite/SqliteRunStore.js +24 -5
- package/dist/runstore/sqlite/migrations/011-email-dispatch-key.d.ts +7 -0
- package/dist/runstore/sqlite/migrations/011-email-dispatch-key.js +12 -0
- package/dist/runstore/sqlite/migrations/index.js +2 -0
- package/dist/runtime/credentialBinding.d.ts +7 -4
- package/dist/runtime/credentialBinding.js +20 -31
- package/dist/runtime/pipelineRunner.d.ts +3 -0
- package/dist/runtime/pipelineRunner.js +11 -0
- package/dist/runtime/pipelineScheduler.js +4 -0
- package/dist/runtime/runManager.d.ts +1 -0
- package/dist/runtime/runManager.js +36 -5
- package/dist/runtime/settingsFile.d.ts +2 -2
- package/dist/runtime/settingsFile.js +5 -2
- package/dist/runtime/stageAttemptBootstrap.js +14 -0
- package/dist/runtime/stageProcessLauncher.d.ts +5 -0
- package/dist/runtime/stageProcessLauncher.js +55 -3
- package/dist/server/bootstrap.d.ts +7 -0
- package/dist/server/bootstrap.js +23 -0
- package/dist/server/emailRoutes.d.ts +5 -0
- package/dist/server/emailRoutes.js +136 -0
- package/dist/server/http.d.ts +2 -0
- package/dist/server/http.js +16 -0
- package/dist/tools/readEmail.d.ts +77 -0
- package/dist/tools/readEmail.js +31 -0
- package/dist/tools/replyEmail.d.ts +31 -0
- package/dist/tools/replyEmail.js +25 -0
- package/dist/tools/sendEmail.d.ts +43 -0
- package/dist/tools/sendEmail.js +26 -0
- package/dist/types/stage.d.ts +4 -0
- package/dist/ui/assets/index-59cjF0OA.js +133 -0
- package/dist/ui/index.html +1 -1
- package/examples/conditional-fork/decide.yaml +1 -1
- package/examples/github-release/README.md +17 -3
- package/examples/hello-email/README.md +23 -0
- package/examples/hello-email/hello-email-reply.pipeline.yaml +6 -0
- package/examples/hello-email/hello-email.pipeline.yaml +6 -0
- package/examples/hello-email/hello-email.task.yaml +6 -0
- package/examples/hello-email/reply-hello-email.yaml +63 -0
- package/examples/hello-email/send-hello-email.yaml +37 -0
- package/package.json +10 -5
- package/dist/ui/assets/index-CntB4bZR.js +0 -133
|
@@ -1,7 +1,15 @@
|
|
|
1
1
|
import { type StageProviderSupport } from "./providerSupport.js";
|
|
2
2
|
export declare function cursorExtensionEntryInPackage(packageRoot: string): string | undefined;
|
|
3
|
+
export declare function resolveBundledCursorExtensionPath(): string | undefined;
|
|
4
|
+
/** `$STAGEFLOW_HOME/agent/npm` install (Pi-style); after bundled dependency. */
|
|
5
|
+
export declare function resolveAgentHomeCursorExtensionPath(): string | undefined;
|
|
3
6
|
export declare function resolveCursorExtensionPath(): string | undefined;
|
|
4
7
|
export declare function isCursorModelRef(modelRef: string): boolean;
|
|
5
8
|
export declare function workshopCursorBridgeHint(toolNames: readonly string[]): string;
|
|
6
9
|
export declare function cursorBridgePrompt(message: string, modelId: string | undefined, toolNames: readonly string[]): string;
|
|
7
10
|
export declare const cursorProviderSupport: StageProviderSupport;
|
|
11
|
+
export declare function readCursorApiKey(env?: NodeJS.ProcessEnv): string | undefined;
|
|
12
|
+
export declare function getCursorHostStatus(env?: NodeJS.ProcessEnv): {
|
|
13
|
+
cursorSdkReady: boolean;
|
|
14
|
+
cursorApiKeyConfigured: boolean;
|
|
15
|
+
};
|
|
@@ -5,10 +5,13 @@
|
|
|
5
5
|
* module's prepare()/env sealing path.
|
|
6
6
|
*/
|
|
7
7
|
import { existsSync, readFileSync } from "node:fs";
|
|
8
|
-
import
|
|
8
|
+
import { createRequire } from "node:module";
|
|
9
9
|
import path from "node:path";
|
|
10
10
|
import { fileURLToPath } from "node:url";
|
|
11
|
+
import { readSecretFromEnvOrFile } from "../config/secretFromEnvOrFile.js";
|
|
12
|
+
import { globalStageflowHome } from "../project/globalHome.js";
|
|
11
13
|
import { registerProviderSupport, } from "./providerSupport.js";
|
|
14
|
+
import { stageflowAgentAuthPath } from "../runtime/credentialBinding.js";
|
|
12
15
|
const CURSOR_SETTING_SOURCES_ENV = "PI_CURSOR_SETTING_SOURCES";
|
|
13
16
|
/**
|
|
14
17
|
* Locate the pi-cursor-sdk extension entry without opening full global package
|
|
@@ -17,21 +20,18 @@ const CURSOR_SETTING_SOURCES_ENV = "PI_CURSOR_SETTING_SOURCES";
|
|
|
17
20
|
* Resolution order:
|
|
18
21
|
* 1. STAGEFLOW_CURSOR_EXTENSION (absolute path to the extension .ts/.js)
|
|
19
22
|
* 2. Path package from the Pi agent settings.json (same source interactive pi uses)
|
|
20
|
-
* 3. npm
|
|
23
|
+
* 3. npm dependency `pi-cursor-sdk` bundled with the Stageflow install (repo or global)
|
|
24
|
+
* 4. npm install under <stageflow-agent>/npm/node_modules/pi-cursor-sdk
|
|
21
25
|
* (`dist/index.js` for 0.3+, `src/index.ts` for older publishes)
|
|
22
|
-
*
|
|
26
|
+
* 5. Sibling checkout at ../pi-cursor-sdk relative to this repo
|
|
23
27
|
*
|
|
24
|
-
* A stage worker's HOME is an empty attempt directory
|
|
25
|
-
*
|
|
26
|
-
* os.homedir().
|
|
28
|
+
* A stage worker's HOME is an empty attempt directory; the agent dir comes from
|
|
29
|
+
* STAGEFLOW_AGENT_AUTH_PATH when the Host set it.
|
|
27
30
|
*/
|
|
28
31
|
const CURSOR_PACKAGE_ENTRIES = ["dist/index.js", "src/index.ts"];
|
|
29
|
-
/**
|
|
32
|
+
/** Stageflow agent dir ($STAGEFLOW_HOME/agent). Stage workers must not use os.homedir(): HOME is the attempt dir. */
|
|
30
33
|
function piAgentDir() {
|
|
31
|
-
|
|
32
|
-
if (auth)
|
|
33
|
-
return path.dirname(path.resolve(auth));
|
|
34
|
-
return path.join(os.homedir(), ".pi", "agent");
|
|
34
|
+
return path.dirname(stageflowAgentAuthPath());
|
|
35
35
|
}
|
|
36
36
|
export function cursorExtensionEntryInPackage(packageRoot) {
|
|
37
37
|
for (const rel of CURSOR_PACKAGE_ENTRIES) {
|
|
@@ -42,6 +42,21 @@ export function cursorExtensionEntryInPackage(packageRoot) {
|
|
|
42
42
|
}
|
|
43
43
|
return undefined;
|
|
44
44
|
}
|
|
45
|
+
export function resolveBundledCursorExtensionPath() {
|
|
46
|
+
const require = createRequire(import.meta.url);
|
|
47
|
+
let packageRoot;
|
|
48
|
+
try {
|
|
49
|
+
packageRoot = path.dirname(require.resolve("pi-cursor-sdk/package.json"));
|
|
50
|
+
}
|
|
51
|
+
catch {
|
|
52
|
+
return undefined;
|
|
53
|
+
}
|
|
54
|
+
return cursorExtensionEntryInPackage(packageRoot);
|
|
55
|
+
}
|
|
56
|
+
/** `$STAGEFLOW_HOME/agent/npm` install (Pi-style); after bundled dependency. */
|
|
57
|
+
export function resolveAgentHomeCursorExtensionPath() {
|
|
58
|
+
return cursorExtensionEntryInPackage(path.join(piAgentDir(), "npm", "node_modules", "pi-cursor-sdk"));
|
|
59
|
+
}
|
|
45
60
|
export function resolveCursorExtensionPath() {
|
|
46
61
|
const fromEnv = process.env.STAGEFLOW_CURSOR_EXTENSION?.trim();
|
|
47
62
|
if (fromEnv && existsSync(fromEnv)) {
|
|
@@ -51,7 +66,11 @@ export function resolveCursorExtensionPath() {
|
|
|
51
66
|
if (fromSettings) {
|
|
52
67
|
return fromSettings;
|
|
53
68
|
}
|
|
54
|
-
const
|
|
69
|
+
const bundled = resolveBundledCursorExtensionPath();
|
|
70
|
+
if (bundled) {
|
|
71
|
+
return bundled;
|
|
72
|
+
}
|
|
73
|
+
const npmEntry = resolveAgentHomeCursorExtensionPath();
|
|
55
74
|
if (npmEntry) {
|
|
56
75
|
return npmEntry;
|
|
57
76
|
}
|
|
@@ -78,7 +97,7 @@ function sealCursorSettingSources() {
|
|
|
78
97
|
function missingExtensionReason(modelRef) {
|
|
79
98
|
return [
|
|
80
99
|
`Model "${modelRef}" requires pi-cursor-sdk, but no extension entry was found.`,
|
|
81
|
-
|
|
100
|
+
`Install Stageflow with npm (includes pi-cursor-sdk), or set STAGEFLOW_CURSOR_EXTENSION`,
|
|
82
101
|
"to the absolute path of pi-cursor-sdk/dist/index.js (or src/index.ts).",
|
|
83
102
|
"Also ensure a Cursor SDK API key is available via Pi /login or CURSOR_API_KEY.",
|
|
84
103
|
].join(" ");
|
|
@@ -122,6 +141,37 @@ export const cursorProviderSupport = {
|
|
|
122
141
|
].join(" ");
|
|
123
142
|
},
|
|
124
143
|
};
|
|
144
|
+
const CURSOR_API_KEY_FILE_NAME = "cursor-api-key";
|
|
145
|
+
export function readCursorApiKey(env = process.env) {
|
|
146
|
+
let fromEnv;
|
|
147
|
+
try {
|
|
148
|
+
fromEnv = readSecretFromEnvOrFile(env, "CURSOR_API_KEY");
|
|
149
|
+
}
|
|
150
|
+
catch {
|
|
151
|
+
return undefined;
|
|
152
|
+
}
|
|
153
|
+
if (fromEnv !== undefined && fromEnv.trim() !== "") {
|
|
154
|
+
return fromEnv.trim();
|
|
155
|
+
}
|
|
156
|
+
const filePath = path.join(globalStageflowHome(), "agent", CURSOR_API_KEY_FILE_NAME);
|
|
157
|
+
if (!existsSync(filePath)) {
|
|
158
|
+
return undefined;
|
|
159
|
+
}
|
|
160
|
+
try {
|
|
161
|
+
const raw = readFileSync(filePath, "utf8").replace(/\r?\n$/, "").trim();
|
|
162
|
+
return raw.length > 0 ? raw : undefined;
|
|
163
|
+
}
|
|
164
|
+
catch {
|
|
165
|
+
return undefined;
|
|
166
|
+
}
|
|
167
|
+
}
|
|
168
|
+
export function getCursorHostStatus(env = process.env) {
|
|
169
|
+
const key = readCursorApiKey(env);
|
|
170
|
+
return {
|
|
171
|
+
cursorSdkReady: resolveCursorExtensionPath() !== undefined,
|
|
172
|
+
cursorApiKeyConfigured: key !== undefined,
|
|
173
|
+
};
|
|
174
|
+
}
|
|
125
175
|
registerProviderSupport(cursorProviderSupport);
|
|
126
176
|
function resolveFromPiSettings() {
|
|
127
177
|
const agentDir = piAgentDir();
|
package/dist/agent/piAdapter.js
CHANGED
|
@@ -44,6 +44,9 @@ import { attemptContext, noAttemptContext, } from "../runtime/stageAttemptContex
|
|
|
44
44
|
import { createEmitStageEnvelopeTool, } from "../tools/emitStageEnvelope.js";
|
|
45
45
|
import { createAskOperatorTool, } from "../tools/askOperator.js";
|
|
46
46
|
import { createWriteStageArtifactTool } from "../tools/writeStageArtifact.js";
|
|
47
|
+
import { createSendEmailTool } from "../tools/sendEmail.js";
|
|
48
|
+
import { createReplyEmailTool } from "../tools/replyEmail.js";
|
|
49
|
+
import { createGetEmailTool, createSearchEmailTool, createDownloadEmailAttachmentTool } from "../tools/readEmail.js";
|
|
47
50
|
import "./cursorProvider.js";
|
|
48
51
|
import { findProviderSupport } from "./providerSupport.js";
|
|
49
52
|
import { mapSessionEventToActivity, readActivityVerbose } from "./activity.js";
|
|
@@ -479,6 +482,9 @@ function buildUserPrompt(input, emitToolName, emitHintOverride, artifactToolName
|
|
|
479
482
|
].join("\n")
|
|
480
483
|
: `Create factory stage artifacts under ${attemptArtifactsPath} relative to the run folder.`;
|
|
481
484
|
return [
|
|
485
|
+
...(input.stage.email?.length ? [
|
|
486
|
+
`Stageflow email permissions: ${JSON.stringify(input.stage.email)}. Use only declared accounts and operations. Use a stable operationKey for each intended message. Provider acceptance does not prove delivery. Never resend an unknown submission automatically.`,
|
|
487
|
+
] : []),
|
|
482
488
|
`Task id: ${input.task.id}`,
|
|
483
489
|
`Stage id: ${runtimeStageId(input)}`,
|
|
484
490
|
`Goal: ${input.task.goal}`,
|
|
@@ -908,6 +914,21 @@ async function prepareStageSessionWiring(input, sessionManager, existingAskWaitC
|
|
|
908
914
|
if (attached.extensionFactories !== undefined) {
|
|
909
915
|
tools.push(...collectMcpExtensionToolNames(loader));
|
|
910
916
|
}
|
|
917
|
+
if (input.email) {
|
|
918
|
+
const permitted = new Set(input.stage.email?.flatMap(permission => permission.operations));
|
|
919
|
+
const definitions = [
|
|
920
|
+
permitted.has("send") ? createSendEmailTool(input.email) : undefined,
|
|
921
|
+
permitted.has("reply") ? createReplyEmailTool(input.email) : undefined,
|
|
922
|
+
permitted.has("search") ? createSearchEmailTool(input.email) : undefined,
|
|
923
|
+
permitted.has("getMessage") ? createGetEmailTool(input.email) : undefined,
|
|
924
|
+
permitted.has("downloadAttachment") ? createDownloadEmailAttachmentTool(input.email) : undefined,
|
|
925
|
+
];
|
|
926
|
+
for (const definition of definitions)
|
|
927
|
+
if (definition) {
|
|
928
|
+
customTools.push(defineTool(definition));
|
|
929
|
+
tools.push(definition.name);
|
|
930
|
+
}
|
|
931
|
+
}
|
|
911
932
|
return {
|
|
912
933
|
sessionManager,
|
|
913
934
|
modelRuntime,
|
package/dist/agent/port.d.ts
CHANGED
|
@@ -7,6 +7,7 @@ import type { TaskFile } from "../types/task.js";
|
|
|
7
7
|
import type { StageActivityEvent } from "./activity.js";
|
|
8
8
|
import type { ResolvedMcpServers } from "../config/resolveStageMcpServers.js";
|
|
9
9
|
import type { StageRoots } from "../runtime/stageRoots.js";
|
|
10
|
+
import type { StageEmail } from "../email/host.js";
|
|
10
11
|
import type { QaExchange } from "../hitl/qaTrail.js";
|
|
11
12
|
import type { StageUsage } from "../types/usage.js";
|
|
12
13
|
/** Opaque to runtime; adapters interpret. */
|
|
@@ -63,6 +64,7 @@ export type StageRunInput = {
|
|
|
63
64
|
/** Optional raw assistant-text-delta sink, fired alongside onActivity, not instead of it. */
|
|
64
65
|
onAssistantTextDelta?: (delta: string) => void;
|
|
65
66
|
skillFilePath?: string;
|
|
67
|
+
email?: StageEmail;
|
|
66
68
|
/** Bundled browser skill; set by the runtime when the stage has `browser`. */
|
|
67
69
|
browserSkillFilePath?: string;
|
|
68
70
|
forkEmitContext?: ForkEmitContext;
|
|
@@ -25,12 +25,16 @@ export type ProvidersListResult = {
|
|
|
25
25
|
via: "pi";
|
|
26
26
|
providers: ProviderSummary[];
|
|
27
27
|
};
|
|
28
|
-
export type
|
|
29
|
-
piHomeUsable: boolean;
|
|
28
|
+
export type ProvidersDetectResult = {
|
|
30
29
|
credentialSource?: CredentialSource;
|
|
31
30
|
provisional: boolean;
|
|
32
31
|
source: CredentialSource;
|
|
32
|
+
authConfigured: boolean;
|
|
33
|
+
cursorSdkReady: boolean;
|
|
34
|
+
cursorApiKeyConfigured: boolean;
|
|
33
35
|
};
|
|
36
|
+
/** @deprecated Use ProvidersDetectResult */
|
|
37
|
+
export type PiHomeDetectResult = ProvidersDetectResult;
|
|
34
38
|
export type ProviderAuthRuntime = {
|
|
35
39
|
getProviders(): readonly Provider[];
|
|
36
40
|
getProvider(providerId: string): Provider | undefined;
|
|
@@ -84,7 +88,7 @@ export type OauthLoginResult = {
|
|
|
84
88
|
};
|
|
85
89
|
export declare function loginWithOauth(cwd: string, providerId: string, interaction: AuthInteraction, ctx?: ProviderAuthContext): Promise<OauthLoginResult>;
|
|
86
90
|
export declare function logoutProvider(cwd: string, providerId: string, ctx?: ProviderAuthContext): Promise<ProviderAuthStatus>;
|
|
87
|
-
export declare function detectPiHome(cwd: string):
|
|
91
|
+
export declare function detectPiHome(cwd: string): ProvidersDetectResult;
|
|
88
92
|
export declare function getCredentialSourceSettings(cwd: string): {
|
|
89
93
|
credentialSource?: CredentialSource;
|
|
90
94
|
binding: {
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { CredentialSynchronizationError, ModelRuntime, } from "@earendil-works/pi-coding-agent";
|
|
2
|
-
import {
|
|
2
|
+
import { parseCredentialSource, resolveCredentialBinding, writeCredentialSourceToContext, isUsableAuthFile, } from "../runtime/credentialBinding.js";
|
|
3
|
+
import { getCursorHostStatus } from "./cursorProvider.js";
|
|
3
4
|
import { readPersistedCredentialSourceFromContext, } from "../runtime/settingsFile.js";
|
|
4
5
|
import { resolveProjectContext } from "../project/resolveProjectContext.js";
|
|
5
6
|
export function makeMutationLock() {
|
|
@@ -251,10 +252,11 @@ export function detectPiHome(cwd) {
|
|
|
251
252
|
const binding = resolveCredentialBinding(projectCtx);
|
|
252
253
|
const persisted = readPersistedCredentialSourceFromContext(projectCtx);
|
|
253
254
|
return {
|
|
254
|
-
piHomeUsable: isUsableAuthFile(piHomeAuthPath()),
|
|
255
255
|
...(persisted !== undefined ? { credentialSource: persisted } : {}),
|
|
256
256
|
provisional: binding.provisional,
|
|
257
257
|
source: binding.source,
|
|
258
|
+
authConfigured: isUsableAuthFile(binding.authPath),
|
|
259
|
+
...getCursorHostStatus(),
|
|
258
260
|
};
|
|
259
261
|
}
|
|
260
262
|
export function getCredentialSourceSettings(cwd) {
|
|
@@ -272,7 +274,7 @@ export function getCredentialSourceSettings(cwd) {
|
|
|
272
274
|
export function setCredentialSource(cwd, credentialSource) {
|
|
273
275
|
const parsed = parseCredentialSource(credentialSource);
|
|
274
276
|
if (parsed === undefined) {
|
|
275
|
-
throw new ProviderAuthError('credentialSource must be "
|
|
277
|
+
throw new ProviderAuthError('credentialSource must be "sf_owned"', 400);
|
|
276
278
|
}
|
|
277
279
|
const projectCtx = resolveProjectContext(cwd);
|
|
278
280
|
writeCredentialSourceToContext(projectCtx, parsed);
|
|
@@ -215,6 +215,13 @@ async function freeDiskCheck(home, env) {
|
|
|
215
215
|
const size = await readFilesystemSize(home);
|
|
216
216
|
const floor = resolveMinFreeDiskFloor(env.STAGEFLOW_MIN_FREE_DISK_BYTES, size.totalBytes);
|
|
217
217
|
const freeGiB = (size.freeBytes / (1024 * 1024 * 1024)).toFixed(2);
|
|
218
|
+
if (floor === 0) {
|
|
219
|
+
return {
|
|
220
|
+
id: "free_disk",
|
|
221
|
+
status: "pass",
|
|
222
|
+
message: `${home} has ${freeGiB} GiB free (free-disk admission disabled)`,
|
|
223
|
+
};
|
|
224
|
+
}
|
|
218
225
|
if (size.freeBytes < floor) {
|
|
219
226
|
return {
|
|
220
227
|
id: "free_disk",
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import type { AuthInteraction } from "@earendil-works/pi-ai";
|
|
2
2
|
import { type ProviderAuthContext } from "../agent/providerAuth.js";
|
|
3
|
-
export declare const PROVIDERS_USAGE = "Usage:\n sf providers list\n sf providers status [--provider <id>]\n sf providers detect\n sf providers source [get | set
|
|
3
|
+
export declare const PROVIDERS_USAGE = "Usage:\n sf providers list\n sf providers status [--provider <id>]\n sf providers detect\n sf providers source [get | set sf_owned]\n sf providers login <providerId> [--type api_key|oauth] [--api-key-env <VAR>]\n sf providers logout <providerId>";
|
|
4
4
|
export type ProvidersCommandIo = {
|
|
5
5
|
log: (line: string) => void;
|
|
6
6
|
error: (line: string) => void;
|
|
@@ -6,7 +6,7 @@ export const PROVIDERS_USAGE = `Usage:
|
|
|
6
6
|
sf providers list
|
|
7
7
|
sf providers status [--provider <id>]
|
|
8
8
|
sf providers detect
|
|
9
|
-
sf providers source [get | set
|
|
9
|
+
sf providers source [get | set sf_owned]
|
|
10
10
|
sf providers login <providerId> [--type api_key|oauth] [--api-key-env <VAR>]
|
|
11
11
|
sf providers logout <providerId>`;
|
|
12
12
|
const defaultIo = {
|
|
@@ -167,7 +167,7 @@ export async function runProvidersCommand(args, cwd, io = {}, ctx = defaultConte
|
|
|
167
167
|
? `credentialSource=${detected.credentialSource}`
|
|
168
168
|
: "credentialSource=(unset)";
|
|
169
169
|
out.log([
|
|
170
|
-
`
|
|
170
|
+
`authConfigured=${detected.authConfigured}`,
|
|
171
171
|
sourceLine,
|
|
172
172
|
`provisional=${detected.provisional}`,
|
|
173
173
|
`bindingSource=${detected.source}`,
|
|
@@ -188,8 +188,8 @@ export async function runProvidersCommand(args, cwd, io = {}, ctx = defaultConte
|
|
|
188
188
|
}
|
|
189
189
|
if (action === "set") {
|
|
190
190
|
const value = parsed.positionals[1];
|
|
191
|
-
if (value !== "
|
|
192
|
-
out.error('source set requires
|
|
191
|
+
if (value !== "sf_owned") {
|
|
192
|
+
out.error('source set requires sf_owned');
|
|
193
193
|
out.error(PROVIDERS_USAGE);
|
|
194
194
|
return 1;
|
|
195
195
|
}
|
package/dist/cli.js
CHANGED
|
@@ -69,7 +69,7 @@ const USAGE = `Usage:
|
|
|
69
69
|
sf providers list
|
|
70
70
|
sf providers status [--provider <id>]
|
|
71
71
|
sf providers detect
|
|
72
|
-
sf providers source [get | set
|
|
72
|
+
sf providers source [get | set sf_owned]
|
|
73
73
|
sf providers login <providerId> [--type api_key|oauth] [--api-key-env <VAR>]
|
|
74
74
|
sf providers logout <providerId>
|
|
75
75
|
sf skills list
|
package/dist/config/loadStage.js
CHANGED
|
@@ -7,6 +7,7 @@ import { parseModelField } from "./modelField.js";
|
|
|
7
7
|
import { allowLegacyYamlAuthoring, dialectWarningForDocument, legacyAuthoringRejected, presentLegacyKeys, } from "./legacyYaml.js";
|
|
8
8
|
import { parsePreEmitChecks } from "./parsePreEmitChecks.js";
|
|
9
9
|
import { readYamlObject } from "./readYamlObject.js";
|
|
10
|
+
import { z } from "zod";
|
|
10
11
|
import { parseStageSecrets } from "../runtime/stageSecretDecl.js";
|
|
11
12
|
import { parseToolRequires } from "./toolRequires.js";
|
|
12
13
|
import { parseStageBrowser } from "./stageBrowser.js";
|
|
@@ -140,6 +141,15 @@ function parseStageFields(raw, label, entryId, deferSchemaRefs, requireIo, compi
|
|
|
140
141
|
id: entryId,
|
|
141
142
|
system_prompt: raw.system_prompt,
|
|
142
143
|
};
|
|
144
|
+
if (raw.email !== undefined) {
|
|
145
|
+
const permissions = z.array(z.object({ accountId: z.string().min(1).max(200),
|
|
146
|
+
operations: z.array(z.enum(["send", "reply", "getMessage", "search", "downloadAttachment"])).min(1).max(5),
|
|
147
|
+
}).strict()).max(20).safeParse(raw.email);
|
|
148
|
+
if (!permissions.success || new Set(permissions.data.map(permission => permission.accountId)).size !== permissions.data.length) {
|
|
149
|
+
return loadFailure([{ code: "stage.invalid_shape", message: `Invalid stage ${label}: invalid email permissions`, category: "stage", stageId: entryId }]);
|
|
150
|
+
}
|
|
151
|
+
stage.email = permissions.data;
|
|
152
|
+
}
|
|
143
153
|
if (raw.clone_actions !== undefined) {
|
|
144
154
|
return loadFailure([
|
|
145
155
|
{
|
|
@@ -28,6 +28,7 @@ export const WIRING_KEYS = new Set([
|
|
|
28
28
|
]);
|
|
29
29
|
/** Stage body keys. Target: `io` / `verify`. IR/legacy: `payload_schema`, `pre_emit_checks`, `clone_input_schema`. */
|
|
30
30
|
export const BODY_KEYS = new Set([
|
|
31
|
+
"email",
|
|
31
32
|
"system_prompt",
|
|
32
33
|
"model",
|
|
33
34
|
"payload_schema",
|
|
@@ -0,0 +1,109 @@
|
|
|
1
|
+
import { z } from "zod";
|
|
2
|
+
import { type EmailAccountStatus } from "./port.js";
|
|
3
|
+
declare const connectionSchema: z.ZodObject<{
|
|
4
|
+
host: z.ZodString;
|
|
5
|
+
port: z.ZodNumber;
|
|
6
|
+
username: z.ZodString;
|
|
7
|
+
tls: z.ZodDefault<z.ZodEnum<{
|
|
8
|
+
implicit: "implicit";
|
|
9
|
+
none: "none";
|
|
10
|
+
starttls: "starttls";
|
|
11
|
+
}>>;
|
|
12
|
+
auth: z.ZodObject<{
|
|
13
|
+
type: z.ZodEnum<{
|
|
14
|
+
oauth2: "oauth2";
|
|
15
|
+
password: "password";
|
|
16
|
+
}>;
|
|
17
|
+
secretRef: z.ZodString;
|
|
18
|
+
expiresAt: z.ZodOptional<z.ZodISODateTime>;
|
|
19
|
+
}, z.core.$strict>;
|
|
20
|
+
}, z.core.$strict>;
|
|
21
|
+
declare const accountSchema: z.ZodObject<{
|
|
22
|
+
displayName: z.ZodString;
|
|
23
|
+
address: z.ZodEmail;
|
|
24
|
+
enabled: z.ZodDefault<z.ZodBoolean>;
|
|
25
|
+
adapter: z.ZodDefault<z.ZodLiteral<"local">>;
|
|
26
|
+
folders: z.ZodDefault<z.ZodArray<z.ZodString>>;
|
|
27
|
+
senderAliases: z.ZodDefault<z.ZodArray<z.ZodEmail>>;
|
|
28
|
+
imap: z.ZodObject<{
|
|
29
|
+
host: z.ZodString;
|
|
30
|
+
port: z.ZodNumber;
|
|
31
|
+
username: z.ZodString;
|
|
32
|
+
tls: z.ZodDefault<z.ZodEnum<{
|
|
33
|
+
implicit: "implicit";
|
|
34
|
+
none: "none";
|
|
35
|
+
starttls: "starttls";
|
|
36
|
+
}>>;
|
|
37
|
+
auth: z.ZodObject<{
|
|
38
|
+
type: z.ZodEnum<{
|
|
39
|
+
oauth2: "oauth2";
|
|
40
|
+
password: "password";
|
|
41
|
+
}>;
|
|
42
|
+
secretRef: z.ZodString;
|
|
43
|
+
expiresAt: z.ZodOptional<z.ZodISODateTime>;
|
|
44
|
+
}, z.core.$strict>;
|
|
45
|
+
}, z.core.$strict>;
|
|
46
|
+
smtp: z.ZodObject<{
|
|
47
|
+
host: z.ZodString;
|
|
48
|
+
port: z.ZodNumber;
|
|
49
|
+
username: z.ZodString;
|
|
50
|
+
tls: z.ZodDefault<z.ZodEnum<{
|
|
51
|
+
implicit: "implicit";
|
|
52
|
+
none: "none";
|
|
53
|
+
starttls: "starttls";
|
|
54
|
+
}>>;
|
|
55
|
+
auth: z.ZodObject<{
|
|
56
|
+
type: z.ZodEnum<{
|
|
57
|
+
oauth2: "oauth2";
|
|
58
|
+
password: "password";
|
|
59
|
+
}>;
|
|
60
|
+
secretRef: z.ZodString;
|
|
61
|
+
expiresAt: z.ZodOptional<z.ZodISODateTime>;
|
|
62
|
+
}, z.core.$strict>;
|
|
63
|
+
}, z.core.$strict>;
|
|
64
|
+
allowInsecureLocalDevelopment: z.ZodDefault<z.ZodBoolean>;
|
|
65
|
+
connectionTimeoutMs: z.ZodDefault<z.ZodNumber>;
|
|
66
|
+
pollingIntervalMs: z.ZodDefault<z.ZodNumber>;
|
|
67
|
+
reconnectMaxDelayMs: z.ZodDefault<z.ZodNumber>;
|
|
68
|
+
searchWorkLimit: z.ZodDefault<z.ZodNumber>;
|
|
69
|
+
attachmentLimits: z.ZodDefault<z.ZodObject<{
|
|
70
|
+
count: z.ZodDefault<z.ZodNumber>;
|
|
71
|
+
perFileBytes: z.ZodDefault<z.ZodNumber>;
|
|
72
|
+
totalBytes: z.ZodDefault<z.ZodNumber>;
|
|
73
|
+
downloadBytes: z.ZodDefault<z.ZodNumber>;
|
|
74
|
+
}, z.core.$strict>>;
|
|
75
|
+
sentFolder: z.ZodOptional<z.ZodString>;
|
|
76
|
+
sentCopyPolicy: z.ZodDefault<z.ZodEnum<{
|
|
77
|
+
"imap-append": "imap-append";
|
|
78
|
+
"provider-managed": "provider-managed";
|
|
79
|
+
}>>;
|
|
80
|
+
sentCopyMaxBytes: z.ZodDefault<z.ZodNumber>;
|
|
81
|
+
}, z.core.$strict>;
|
|
82
|
+
export type EmailAccountInput = z.input<typeof accountSchema>;
|
|
83
|
+
export type EmailConnection = z.output<typeof connectionSchema>;
|
|
84
|
+
export type EmailAccount = z.output<typeof accountSchema> & {
|
|
85
|
+
accountId: string;
|
|
86
|
+
scope: string;
|
|
87
|
+
};
|
|
88
|
+
/** Host-owned configuration. Reads return copies; mutations replace the file atomically. */
|
|
89
|
+
export declare class EmailAccounts {
|
|
90
|
+
readonly scope: string;
|
|
91
|
+
private readonly file;
|
|
92
|
+
private state;
|
|
93
|
+
private readonly listeners;
|
|
94
|
+
constructor(cwd: string);
|
|
95
|
+
list(): EmailAccount[];
|
|
96
|
+
get(accountId: string, requireEnabled?: boolean): EmailAccount;
|
|
97
|
+
create(input: unknown): EmailAccount;
|
|
98
|
+
update(accountId: string, patch: unknown): EmailAccount;
|
|
99
|
+
remove(accountId: string): void;
|
|
100
|
+
health(accountId: string): EmailAccountStatus | undefined;
|
|
101
|
+
recordHealth(status: EmailAccountStatus): void;
|
|
102
|
+
onChange(listener: (accountId: string) => void): () => void;
|
|
103
|
+
private notify;
|
|
104
|
+
private requireHttpAccount;
|
|
105
|
+
private syncStartupAccounts;
|
|
106
|
+
private persist;
|
|
107
|
+
}
|
|
108
|
+
export declare function resolveEmailSecret(connection: EmailConnection, env?: NodeJS.ProcessEnv): string;
|
|
109
|
+
export {};
|