stageflow 0.30.0 → 0.31.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/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/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/runtime/credentialBinding.d.ts +7 -4
- package/dist/runtime/credentialBinding.js +20 -31
- package/dist/runtime/pipelineScheduler.js +1 -0
- package/dist/runtime/runManager.js +12 -2
- package/dist/runtime/settingsFile.d.ts +2 -2
- package/dist/runtime/settingsFile.js +5 -2
- package/dist/runtime/stageProcessLauncher.d.ts +1 -0
- package/dist/runtime/stageProcessLauncher.js +10 -2
- package/dist/server/bootstrap.js +6 -0
- package/dist/ui/assets/{index-CntB4bZR.js → index-p8Iou3zk.js} +12 -12
- package/dist/ui/index.html +1 -1
- package/examples/conditional-fork/decide.yaml +1 -1
- package/examples/github-release/README.md +17 -3
- package/package.json +5 -4
|
@@ -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();
|
|
@@ -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/package-meta.d.ts
CHANGED
package/dist/package-meta.js
CHANGED
|
@@ -32,7 +32,7 @@ export declare function durableRootDiskBreakdown(durableRoot: string, options?:
|
|
|
32
32
|
export declare function parseDiskBytesThreshold(raw: string | undefined, filesystemTotalBytes: number): number | undefined;
|
|
33
33
|
/**
|
|
34
34
|
* Resolve `STAGEFLOW_MIN_FREE_DISK_BYTES`: bytes or `%` of filesystem.
|
|
35
|
-
* Unset / empty / invalid → `
|
|
35
|
+
* Unset / empty / invalid → `0` (admission check disabled). Set explicitly to enable.
|
|
36
36
|
*/
|
|
37
37
|
export declare function resolveMinFreeDiskFloor(raw: string | undefined, filesystemTotalBytes: number): number;
|
|
38
38
|
/**
|
|
@@ -162,20 +162,15 @@ export function parseDiskBytesThreshold(raw, filesystemTotalBytes) {
|
|
|
162
162
|
return undefined;
|
|
163
163
|
return n;
|
|
164
164
|
}
|
|
165
|
-
const DEFAULT_MIN_FREE_DISK_BYTES = 2 * 1024 * 1024 * 1024;
|
|
166
165
|
/**
|
|
167
166
|
* Resolve `STAGEFLOW_MIN_FREE_DISK_BYTES`: bytes or `%` of filesystem.
|
|
168
|
-
* Unset / empty / invalid → `
|
|
167
|
+
* Unset / empty / invalid → `0` (admission check disabled). Set explicitly to enable.
|
|
169
168
|
*/
|
|
170
169
|
export function resolveMinFreeDiskFloor(raw, filesystemTotalBytes) {
|
|
171
170
|
const parsed = parseDiskBytesThreshold(raw, filesystemTotalBytes);
|
|
172
171
|
if (parsed !== undefined)
|
|
173
172
|
return parsed;
|
|
174
|
-
|
|
175
|
-
? filesystemTotalBytes
|
|
176
|
-
: 0;
|
|
177
|
-
const tenPercent = Math.floor(total * 0.1);
|
|
178
|
-
return Math.max(DEFAULT_MIN_FREE_DISK_BYTES, tenPercent);
|
|
173
|
+
return 0;
|
|
179
174
|
}
|
|
180
175
|
/**
|
|
181
176
|
* One named warning when free space is below `STAGEFLOW_DISK_WARN_BYTES`. Never throws.
|
|
@@ -7,14 +7,17 @@ export type CredentialBinding = {
|
|
|
7
7
|
provisional: boolean;
|
|
8
8
|
};
|
|
9
9
|
export type ResolveCredentialBindingOptions = {
|
|
10
|
-
|
|
10
|
+
agentAuthPath?: string;
|
|
11
11
|
};
|
|
12
12
|
export declare function sfOwnedAgentDir(): string;
|
|
13
13
|
export declare function sfOwnedAuthPath(): string;
|
|
14
|
-
export declare const
|
|
15
|
-
/**
|
|
14
|
+
export declare const STAGEFLOW_AGENT_AUTH_PATH_ENV = "STAGEFLOW_AGENT_AUTH_PATH";
|
|
15
|
+
/** @deprecated Use STAGEFLOW_AGENT_AUTH_PATH_ENV. */
|
|
16
|
+
export declare const PI_HOME_AUTH_PATH_ENV = "STAGEFLOW_AGENT_AUTH_PATH";
|
|
17
|
+
export declare function stageflowAgentAuthPath(override?: string): string;
|
|
18
|
+
/** @deprecated Use stageflowAgentAuthPath */
|
|
16
19
|
export declare function piHomeAuthPath(override?: string): string;
|
|
17
20
|
export declare function isUsableAuthFile(authPath: string): boolean;
|
|
18
21
|
export declare function ensureSfOwnedAuthStore(): string;
|
|
19
|
-
export declare function resolveCredentialBinding(ctx: ProjectContext | string,
|
|
22
|
+
export declare function resolveCredentialBinding(ctx: ProjectContext | string, _options?: ResolveCredentialBindingOptions): CredentialBinding;
|
|
20
23
|
export { readCredentialSourceFromFile, writeCredentialSourceToFile, } from "./settingsFile.js";
|
|
@@ -1,5 +1,4 @@
|
|
|
1
1
|
import { chmodSync, existsSync, readFileSync, writeFileSync } from "node:fs";
|
|
2
|
-
import os from "node:os";
|
|
3
2
|
import path from "node:path";
|
|
4
3
|
import { ensureGlobalHome } from "../project/globalHome.js";
|
|
5
4
|
import { resolveProjectContext, } from "../project/resolveProjectContext.js";
|
|
@@ -11,12 +10,23 @@ export function sfOwnedAgentDir() {
|
|
|
11
10
|
export function sfOwnedAuthPath() {
|
|
12
11
|
return path.join(sfOwnedAgentDir(), "auth.json");
|
|
13
12
|
}
|
|
14
|
-
export const
|
|
15
|
-
/**
|
|
13
|
+
export const STAGEFLOW_AGENT_AUTH_PATH_ENV = "STAGEFLOW_AGENT_AUTH_PATH";
|
|
14
|
+
/** @deprecated Use STAGEFLOW_AGENT_AUTH_PATH_ENV. */
|
|
15
|
+
export const PI_HOME_AUTH_PATH_ENV = STAGEFLOW_AGENT_AUTH_PATH_ENV;
|
|
16
|
+
export function stageflowAgentAuthPath(override) {
|
|
17
|
+
if (override !== undefined) {
|
|
18
|
+
return path.resolve(override);
|
|
19
|
+
}
|
|
20
|
+
const fromEnv = process.env[STAGEFLOW_AGENT_AUTH_PATH_ENV]?.trim() ||
|
|
21
|
+
process.env.STAGEFLOW_PI_HOME_AUTH_PATH?.trim();
|
|
22
|
+
if (fromEnv) {
|
|
23
|
+
return path.resolve(fromEnv);
|
|
24
|
+
}
|
|
25
|
+
return sfOwnedAuthPath();
|
|
26
|
+
}
|
|
27
|
+
/** @deprecated Use stageflowAgentAuthPath */
|
|
16
28
|
export function piHomeAuthPath(override) {
|
|
17
|
-
return (override
|
|
18
|
-
(process.env[PI_HOME_AUTH_PATH_ENV] || undefined) ??
|
|
19
|
-
path.join(os.homedir(), ".pi", "agent", "auth.json"));
|
|
29
|
+
return stageflowAgentAuthPath(override);
|
|
20
30
|
}
|
|
21
31
|
export function isUsableAuthFile(authPath) {
|
|
22
32
|
if (!existsSync(authPath))
|
|
@@ -43,35 +53,14 @@ export function ensureSfOwnedAuthStore() {
|
|
|
43
53
|
}
|
|
44
54
|
return authPath;
|
|
45
55
|
}
|
|
46
|
-
export function resolveCredentialBinding(ctx,
|
|
56
|
+
export function resolveCredentialBinding(ctx, _options = {}) {
|
|
47
57
|
const projectCtx = typeof ctx === "string" ? resolveProjectContext(ctx) : ctx;
|
|
48
|
-
const piHome = piHomeAuthPath(options.piHomeAuthPath);
|
|
49
58
|
const persisted = readCredentialSourceFromContext(projectCtx);
|
|
50
|
-
|
|
51
|
-
if (persisted === "sf_owned") {
|
|
52
|
-
return {
|
|
53
|
-
source: "sf_owned",
|
|
54
|
-
authPath: ensureSfOwnedAuthStore(),
|
|
55
|
-
provisional: false,
|
|
56
|
-
};
|
|
57
|
-
}
|
|
58
|
-
return {
|
|
59
|
-
source: "pi_home",
|
|
60
|
-
authPath: piHome,
|
|
61
|
-
provisional: false,
|
|
62
|
-
};
|
|
63
|
-
}
|
|
64
|
-
if (isUsableAuthFile(piHome)) {
|
|
65
|
-
return {
|
|
66
|
-
source: "pi_home",
|
|
67
|
-
authPath: piHome,
|
|
68
|
-
provisional: true,
|
|
69
|
-
};
|
|
70
|
-
}
|
|
59
|
+
const authPath = ensureSfOwnedAuthStore();
|
|
71
60
|
return {
|
|
72
61
|
source: "sf_owned",
|
|
73
|
-
authPath
|
|
74
|
-
provisional:
|
|
62
|
+
authPath,
|
|
63
|
+
provisional: persisted === undefined,
|
|
75
64
|
};
|
|
76
65
|
}
|
|
77
66
|
export { readCredentialSourceFromFile, writeCredentialSourceToFile, } from "./settingsFile.js";
|
|
@@ -1979,6 +1979,7 @@ export class RunManager {
|
|
|
1979
1979
|
sessionFilePath,
|
|
1980
1980
|
env: stageBinding.env,
|
|
1981
1981
|
bindingKind: stageBinding.kind,
|
|
1982
|
+
...(resumedStage !== undefined ? { model: resumedStage.model } : {}),
|
|
1982
1983
|
...(this.options.operatorCatalog !== undefined
|
|
1983
1984
|
? { operatorCatalog: this.options.operatorCatalog }
|
|
1984
1985
|
: {}),
|
|
@@ -2506,17 +2507,26 @@ export class RunManager {
|
|
|
2506
2507
|
};
|
|
2507
2508
|
}
|
|
2508
2509
|
async checkDiskFloorAdmission() {
|
|
2510
|
+
const raw = process.env.STAGEFLOW_MIN_FREE_DISK_BYTES;
|
|
2511
|
+
if (raw === undefined || raw.trim() === "") {
|
|
2512
|
+
return undefined;
|
|
2513
|
+
}
|
|
2509
2514
|
try {
|
|
2510
2515
|
const readFree = this.options.freeSpaceReader ?? readFilesystemSize;
|
|
2511
2516
|
const size = await readFree(globalStageflowHome());
|
|
2512
|
-
const minFreeBytes = resolveMinFreeDiskFloor(
|
|
2517
|
+
const minFreeBytes = resolveMinFreeDiskFloor(raw, size.totalBytes);
|
|
2518
|
+
if (minFreeBytes === 0) {
|
|
2519
|
+
return undefined;
|
|
2520
|
+
}
|
|
2513
2521
|
if (size.freeBytes < minFreeBytes) {
|
|
2514
2522
|
return this.insufficientDiskFailure(size.freeBytes, minFreeBytes);
|
|
2515
2523
|
}
|
|
2516
2524
|
return undefined;
|
|
2517
2525
|
}
|
|
2518
2526
|
catch (err) {
|
|
2519
|
-
|
|
2527
|
+
if (resolveMinFreeDiskFloor(raw, 0) === 0 && !raw.trim().endsWith("%")) {
|
|
2528
|
+
return undefined;
|
|
2529
|
+
}
|
|
2520
2530
|
return {
|
|
2521
2531
|
ok: false,
|
|
2522
2532
|
reason: `Disk free-space check failed: ${err instanceof Error ? err.message : String(err)}`,
|
|
@@ -2,9 +2,9 @@ import type { ProjectContext } from "../project/resolveProjectContext.js";
|
|
|
2
2
|
import type { StageflowContext } from "../project/resolveStageflowContext.js";
|
|
3
3
|
export declare const SETTINGS_FILE_NAME = "settings.json";
|
|
4
4
|
export declare const INVALID_SLOT_COUNT_MESSAGE = "maxConcurrent must be an integer >= 1";
|
|
5
|
-
export declare const INVALID_CREDENTIAL_SOURCE_MESSAGE = "credentialSource must be \"
|
|
5
|
+
export declare const INVALID_CREDENTIAL_SOURCE_MESSAGE = "credentialSource must be \"sf_owned\"";
|
|
6
6
|
export declare const INVALID_WORKSHOP_MODEL_MESSAGE = "workshopModel must be a non-empty string";
|
|
7
|
-
export type CredentialSource = "
|
|
7
|
+
export type CredentialSource = "sf_owned";
|
|
8
8
|
export type FactorySettings = {
|
|
9
9
|
maxConcurrent?: number;
|
|
10
10
|
credentialSource?: CredentialSource;
|
|
@@ -5,7 +5,7 @@ import { globalStageflowHome } from "../project/globalHome.js";
|
|
|
5
5
|
import { storeRootFor } from "../runstore/paths.js";
|
|
6
6
|
export const SETTINGS_FILE_NAME = "settings.json";
|
|
7
7
|
export const INVALID_SLOT_COUNT_MESSAGE = "maxConcurrent must be an integer >= 1";
|
|
8
|
-
export const INVALID_CREDENTIAL_SOURCE_MESSAGE = 'credentialSource must be "
|
|
8
|
+
export const INVALID_CREDENTIAL_SOURCE_MESSAGE = 'credentialSource must be "sf_owned"';
|
|
9
9
|
export const INVALID_WORKSHOP_MODEL_MESSAGE = "workshopModel must be a non-empty string";
|
|
10
10
|
export function globalSettingsFilePath() {
|
|
11
11
|
return path.join(globalStageflowHome(), SETTINGS_FILE_NAME);
|
|
@@ -29,9 +29,12 @@ export function parseSlotCount(value) {
|
|
|
29
29
|
return value;
|
|
30
30
|
}
|
|
31
31
|
export function parseCredentialSource(value) {
|
|
32
|
-
if (value === "
|
|
32
|
+
if (value === "sf_owned") {
|
|
33
33
|
return value;
|
|
34
34
|
}
|
|
35
|
+
if (value === "pi_home") {
|
|
36
|
+
return "sf_owned";
|
|
37
|
+
}
|
|
35
38
|
return undefined;
|
|
36
39
|
}
|
|
37
40
|
export function parseWorkshopModelSetting(value) {
|
|
@@ -1,9 +1,10 @@
|
|
|
1
1
|
import * as childProcess from "node:child_process";
|
|
2
2
|
import { fileURLToPath } from "node:url";
|
|
3
3
|
import { logger as rootLogger, resolveLogMaxLineBytes, } from "../logging/logger.js";
|
|
4
|
+
import { isCursorModelRef, readCursorApiKey } from "../agent/cursorProvider.js";
|
|
4
5
|
import { BROWSER_ENV_PREFIX } from "../browser/browserHost.js";
|
|
5
6
|
import { PACKAGE_VERSION } from "../package-meta.js";
|
|
6
|
-
import { PI_HOME_AUTH_PATH_ENV,
|
|
7
|
+
import { PI_HOME_AUTH_PATH_ENV, stageflowAgentAuthPath } from "./credentialBinding.js";
|
|
7
8
|
import { redactString } from "../logging/redact.js";
|
|
8
9
|
import { getNamedSecrets } from "../logging/namedSecrets.js";
|
|
9
10
|
import { readMaxActiveStageProcesses, } from "./stageConcurrency.js";
|
|
@@ -338,8 +339,15 @@ export class StageProcessLauncher {
|
|
|
338
339
|
: overlaid;
|
|
339
340
|
const childEnv = {
|
|
340
341
|
...withBrowser,
|
|
341
|
-
[PI_HOME_AUTH_PATH_ENV]:
|
|
342
|
+
[PI_HOME_AUTH_PATH_ENV]: stageflowAgentAuthPath(),
|
|
342
343
|
};
|
|
344
|
+
const cursorApiKey = readCursorApiKey(hostEnv);
|
|
345
|
+
if (isCursorModelRef(input.model ?? "") &&
|
|
346
|
+
cursorApiKey !== undefined &&
|
|
347
|
+
cursorApiKey !== "" &&
|
|
348
|
+
childEnv.CURSOR_API_KEY === undefined) {
|
|
349
|
+
childEnv.CURSOR_API_KEY = cursorApiKey;
|
|
350
|
+
}
|
|
343
351
|
const child = this.forkFn(this.cliEntry, args, {
|
|
344
352
|
cwd: input.rootDir,
|
|
345
353
|
env: { ...childEnv, [SF_STAGE_WORKER]: "1" },
|
package/dist/server/bootstrap.js
CHANGED
|
@@ -20,6 +20,7 @@ import { createRunChangeBus, getRunChangeBusFromWrappedStore, isRunStoreWrapped,
|
|
|
20
20
|
import { createMcpHttpHandler, resolveMcpStateless, } from "../mcp/server.js";
|
|
21
21
|
import { loadHostConfig, } from "../config/hostConfig.js";
|
|
22
22
|
import { bootProviderConfig } from "../agent/bootProviderConfig.js";
|
|
23
|
+
import { readCursorApiKey } from "../agent/cursorProvider.js";
|
|
23
24
|
import { applyPendingRestoreAtBoot, } from "../runstore/restore.js";
|
|
24
25
|
import { assertTmpdirUsable } from "../runstore/assertTmpdir.js";
|
|
25
26
|
import { logger as rootLogger } from "../logging/logger.js";
|
|
@@ -80,6 +81,11 @@ export async function bootstrapStageflowHost(options) {
|
|
|
80
81
|
const ctx = await resolveStageflowContext(invocationCwd);
|
|
81
82
|
const cwd = ctx.invocationCwd;
|
|
82
83
|
ensureGlobalHome();
|
|
84
|
+
const cursorKey = readCursorApiKey(env);
|
|
85
|
+
if (cursorKey !== undefined &&
|
|
86
|
+
(env.CURSOR_API_KEY === undefined || env.CURSOR_API_KEY.trim() === "")) {
|
|
87
|
+
process.env.CURSOR_API_KEY = cursorKey;
|
|
88
|
+
}
|
|
83
89
|
assertTmpdirUsable(env);
|
|
84
90
|
const hostConfig = options.hostConfig ??
|
|
85
91
|
(options.skipHostConfig
|