@starlein/paperclip-plugin-company-wizard 0.4.22 → 0.5.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +19 -0
- package/README.md +6 -4
- package/dist/manifest.js +17 -2
- package/dist/manifest.js.map +2 -2
- package/dist/ui/index.js.map +1 -1
- package/dist/worker.js +109 -12
- package/dist/worker.js.map +2 -2
- package/package.json +1 -1
package/dist/worker.js
CHANGED
|
@@ -12900,7 +12900,7 @@ import { fileURLToPath as fileURLToPath2 } from "node:url";
|
|
|
12900
12900
|
var manifest = {
|
|
12901
12901
|
id: "starlein.paperclip-plugin-company-wizard",
|
|
12902
12902
|
apiVersion: 1,
|
|
12903
|
-
version: "0.
|
|
12903
|
+
version: "0.5.0",
|
|
12904
12904
|
displayName: "Company Wizard",
|
|
12905
12905
|
description: "AI-powered wizard to bootstrap agent companies from composable templates",
|
|
12906
12906
|
author: "Sascha Pietrowski <sp@speednetwork.de>",
|
|
@@ -12943,11 +12943,26 @@ var manifest = {
|
|
|
12943
12943
|
default: "https://github.com/starlein/paperclip-plugin-company-wizard/tree/main/templates",
|
|
12944
12944
|
description: "GitHub tree URL for template downloads. The default is correct for most setups \u2014 only change this if using a custom fork."
|
|
12945
12945
|
},
|
|
12946
|
-
|
|
12946
|
+
aiProvider: {
|
|
12947
12947
|
type: "string",
|
|
12948
|
+
enum: ["anthropic", "openai"],
|
|
12949
|
+
default: "anthropic",
|
|
12950
|
+
description: "AI provider used for company generation. Anthropic uses Claude Opus 5 at max effort; OpenAI uses GPT-5.6 Sol at high reasoning effort."
|
|
12951
|
+
},
|
|
12952
|
+
anthropicApiKey: {
|
|
12953
|
+
// Paperclip's secret picker submits an EnvSecretRefBinding object,
|
|
12954
|
+
// while direct keys and older hosts still submit strings. Keep both
|
|
12955
|
+
// representations schema-valid; the host validates governed bindings
|
|
12956
|
+
// and the worker resolves them before calling Anthropic.
|
|
12957
|
+
type: ["string", "object"],
|
|
12948
12958
|
format: "secret-ref",
|
|
12949
12959
|
description: "Anthropic API key for the AI wizard. Paste a key or select a saved Paperclip company secret; Paperclip stores pasted values as governed secret references."
|
|
12950
12960
|
},
|
|
12961
|
+
openaiApiKey: {
|
|
12962
|
+
type: ["string", "object"],
|
|
12963
|
+
format: "secret-ref",
|
|
12964
|
+
description: "OpenAI API key for GPT/Codex company generation. Paste a key or select a saved Paperclip company secret; required when AI Provider is openai."
|
|
12965
|
+
},
|
|
12951
12966
|
paperclipUrl: {
|
|
12952
12967
|
type: "string",
|
|
12953
12968
|
description: "Paperclip instance URL. Defaults to http://localhost:3100 or the PAPERCLIP_PUBLIC_URL env var."
|
|
@@ -15000,9 +15015,14 @@ function refreshTemplatesCache(cfg, log) {
|
|
|
15000
15015
|
log?.(`\u2713 Refreshed templates cache from ${repoUrl} \u2192 ${targetDir}`);
|
|
15001
15016
|
return targetDir;
|
|
15002
15017
|
}
|
|
15018
|
+
var ANTHROPIC_AI_MODEL = "claude-opus-5";
|
|
15019
|
+
var OPENAI_AI_MODEL = "gpt-5.6-sol";
|
|
15003
15020
|
function isLikelyAnthropicApiKey(value) {
|
|
15004
15021
|
return value.startsWith("sk-ant-");
|
|
15005
15022
|
}
|
|
15023
|
+
function isLikelyOpenAiApiKey(value) {
|
|
15024
|
+
return value.startsWith("sk-");
|
|
15025
|
+
}
|
|
15006
15026
|
async function resolveAnthropicApiKey(ctx, configuredValue, companyId) {
|
|
15007
15027
|
const options = {
|
|
15008
15028
|
...companyId ? { companyId } : {},
|
|
@@ -15025,6 +15045,28 @@ async function resolveAnthropicApiKey(ctx, configuredValue, companyId) {
|
|
|
15025
15045
|
);
|
|
15026
15046
|
}
|
|
15027
15047
|
}
|
|
15048
|
+
async function resolveOpenAiApiKey(ctx, configuredValue, companyId) {
|
|
15049
|
+
const options = {
|
|
15050
|
+
...companyId ? { companyId } : {},
|
|
15051
|
+
configPath: "openaiApiKey"
|
|
15052
|
+
};
|
|
15053
|
+
if (configuredValue && typeof configuredValue === "object" && configuredValue.type === "secret_ref" && typeof configuredValue.secretId === "string") {
|
|
15054
|
+
return ctx.secrets.resolve(configuredValue, options);
|
|
15055
|
+
}
|
|
15056
|
+
if (typeof configuredValue !== "string") return "";
|
|
15057
|
+
const value = configuredValue.trim();
|
|
15058
|
+
if (!value) return "";
|
|
15059
|
+
if (isLikelyOpenAiApiKey(value)) return value;
|
|
15060
|
+
try {
|
|
15061
|
+
const resolved = await ctx.secrets.resolve(value, options);
|
|
15062
|
+
return resolved.trim();
|
|
15063
|
+
} catch (err) {
|
|
15064
|
+
const detail = err instanceof Error ? err.message : String(err);
|
|
15065
|
+
throw new Error(
|
|
15066
|
+
`OpenAI API key could not be resolved. Re-save the plugin setting with a valid OpenAI API key. ${detail}`
|
|
15067
|
+
);
|
|
15068
|
+
}
|
|
15069
|
+
}
|
|
15028
15070
|
async function callAnthropic(apiKey, system, messages) {
|
|
15029
15071
|
try {
|
|
15030
15072
|
const response = await fetch("https://api.anthropic.com/v1/messages", {
|
|
@@ -15035,11 +15077,13 @@ async function callAnthropic(apiKey, system, messages) {
|
|
|
15035
15077
|
"anthropic-version": "2023-06-01"
|
|
15036
15078
|
},
|
|
15037
15079
|
body: JSON.stringify({
|
|
15038
|
-
model:
|
|
15080
|
+
model: ANTHROPIC_AI_MODEL,
|
|
15039
15081
|
// High ceiling so the wizard's final config — which reproduces the user's
|
|
15040
15082
|
// full spec in the first goal — is never truncated mid-JSON. This is only
|
|
15041
15083
|
// an upper bound; generation time tracks the tokens actually produced.
|
|
15042
|
-
max_tokens:
|
|
15084
|
+
max_tokens: 65536,
|
|
15085
|
+
thinking: { type: "adaptive" },
|
|
15086
|
+
output_config: { effort: "max" },
|
|
15043
15087
|
...system ? { system } : {},
|
|
15044
15088
|
messages
|
|
15045
15089
|
})
|
|
@@ -15049,7 +15093,53 @@ async function callAnthropic(apiKey, system, messages) {
|
|
|
15049
15093
|
return { text: "", error: `Anthropic API error (${response.status}): ${body}` };
|
|
15050
15094
|
}
|
|
15051
15095
|
const data = await response.json();
|
|
15052
|
-
|
|
15096
|
+
if (data.stop_reason && data.stop_reason !== "end_turn" && data.stop_reason !== "stop_sequence") {
|
|
15097
|
+
return {
|
|
15098
|
+
text: "",
|
|
15099
|
+
error: `Anthropic generation stopped before completion (${data.stop_reason}).`
|
|
15100
|
+
};
|
|
15101
|
+
}
|
|
15102
|
+
const text = data.content?.filter((block) => block.type === "text" && typeof block.text === "string").map((block) => block.text).join("") || "";
|
|
15103
|
+
return text ? { text } : { text: "", error: "Anthropic returned no text output." };
|
|
15104
|
+
} catch (err) {
|
|
15105
|
+
return { text: "", error: err instanceof Error ? err.message : String(err) };
|
|
15106
|
+
}
|
|
15107
|
+
}
|
|
15108
|
+
async function callOpenAi(apiKey, system, messages) {
|
|
15109
|
+
try {
|
|
15110
|
+
const input = Array.isArray(messages) ? messages.map((message) => {
|
|
15111
|
+
const record = message;
|
|
15112
|
+
return { role: record.role, content: record.content };
|
|
15113
|
+
}) : messages;
|
|
15114
|
+
const response = await fetch("https://api.openai.com/v1/responses", {
|
|
15115
|
+
method: "POST",
|
|
15116
|
+
headers: {
|
|
15117
|
+
"Content-Type": "application/json",
|
|
15118
|
+
Authorization: `Bearer ${apiKey}`
|
|
15119
|
+
},
|
|
15120
|
+
body: JSON.stringify({
|
|
15121
|
+
model: OPENAI_AI_MODEL,
|
|
15122
|
+
reasoning: { effort: "high" },
|
|
15123
|
+
max_output_tokens: 65536,
|
|
15124
|
+
...typeof system === "string" && system ? { instructions: system } : {},
|
|
15125
|
+
input
|
|
15126
|
+
})
|
|
15127
|
+
});
|
|
15128
|
+
if (!response.ok) {
|
|
15129
|
+
const body = await response.text();
|
|
15130
|
+
return { text: "", error: `OpenAI API error (${response.status}): ${body}` };
|
|
15131
|
+
}
|
|
15132
|
+
const data = await response.json();
|
|
15133
|
+
if (data.status && data.status !== "completed") {
|
|
15134
|
+
const reason = data.incomplete_details?.reason || data.status;
|
|
15135
|
+
return { text: "", error: `OpenAI generation stopped before completion (${reason}).` };
|
|
15136
|
+
}
|
|
15137
|
+
const content = data.output?.flatMap((item) => item.content ?? []) ?? [];
|
|
15138
|
+
if (content.some((part) => part.type === "refusal")) {
|
|
15139
|
+
return { text: "", error: "OpenAI refused the generation request." };
|
|
15140
|
+
}
|
|
15141
|
+
const text = data.output_text || content.filter((part) => part.type === "output_text" && typeof part.text === "string").map((part) => part.text).join("");
|
|
15142
|
+
return text ? { text } : { text: "", error: "OpenAI returned no text output." };
|
|
15053
15143
|
} catch (err) {
|
|
15054
15144
|
return { text: "", error: err instanceof Error ? err.message : String(err) };
|
|
15055
15145
|
}
|
|
@@ -15907,18 +15997,20 @@ var plugin = definePlugin({
|
|
|
15907
15997
|
}
|
|
15908
15998
|
const cfg = await ctx.config.get() ?? {};
|
|
15909
15999
|
const companyId = typeof params.companyId === "string" ? params.companyId : void 0;
|
|
15910
|
-
const
|
|
16000
|
+
const provider = cfg.aiProvider === "openai" ? "openai" : "anthropic";
|
|
16001
|
+
const apiKey = provider === "openai" ? await resolveOpenAiApiKey(ctx, cfg.openaiApiKey, companyId) : await resolveAnthropicApiKey(ctx, cfg.anthropicApiKey, companyId);
|
|
15911
16002
|
if (!apiKey) {
|
|
15912
16003
|
return {
|
|
15913
16004
|
text: "",
|
|
15914
|
-
error: "Anthropic API key not configured. Add it in plugin settings (anthropicApiKey)."
|
|
16005
|
+
error: provider === "openai" ? "OpenAI API key not configured. Add it in plugin settings (openaiApiKey)." : "Anthropic API key not configured. Add it in plugin settings (anthropicApiKey)."
|
|
15915
16006
|
};
|
|
15916
16007
|
}
|
|
16008
|
+
const generate = () => provider === "openai" ? callOpenAi(apiKey, params.system, params.messages) : callAnthropic(apiKey, params.system, params.messages);
|
|
15917
16009
|
if (params.mode === "start") {
|
|
15918
16010
|
sweepAiChatJobs();
|
|
15919
16011
|
const jobId = `aichat-${Date.now()}-${Math.random().toString(36).slice(2, 10)}`;
|
|
15920
16012
|
aiChatJobs.set(jobId, { status: "pending", createdAt: Date.now() });
|
|
15921
|
-
void
|
|
16013
|
+
void generate().then((result) => {
|
|
15922
16014
|
aiChatJobs.set(
|
|
15923
16015
|
jobId,
|
|
15924
16016
|
result.error ? { status: "error", error: result.error, createdAt: Date.now() } : { status: "done", text: result.text, createdAt: Date.now() }
|
|
@@ -15926,7 +16018,7 @@ var plugin = definePlugin({
|
|
|
15926
16018
|
});
|
|
15927
16019
|
return { jobId, status: "pending" };
|
|
15928
16020
|
}
|
|
15929
|
-
return await
|
|
16021
|
+
return await generate();
|
|
15930
16022
|
} catch (err) {
|
|
15931
16023
|
return { text: "", error: err instanceof Error ? err.message : String(err) };
|
|
15932
16024
|
}
|
|
@@ -15935,14 +16027,19 @@ var plugin = definePlugin({
|
|
|
15935
16027
|
try {
|
|
15936
16028
|
const cfg = await ctx.config.get() ?? {};
|
|
15937
16029
|
const companyId = typeof params.companyId === "string" ? params.companyId : void 0;
|
|
15938
|
-
const
|
|
16030
|
+
const provider = cfg.aiProvider === "openai" ? "openai" : "anthropic";
|
|
16031
|
+
const apiKey = provider === "openai" ? await resolveOpenAiApiKey(ctx, cfg.openaiApiKey, companyId) : await resolveAnthropicApiKey(ctx, cfg.anthropicApiKey, companyId);
|
|
15939
16032
|
if (!apiKey) {
|
|
15940
16033
|
return {
|
|
15941
16034
|
ok: false,
|
|
15942
|
-
error: "Anthropic API key not configured. Add it in plugin settings (anthropicApiKey)."
|
|
16035
|
+
error: provider === "openai" ? "OpenAI API key not configured. Add it in plugin settings (openaiApiKey)." : "Anthropic API key not configured. Add it in plugin settings (anthropicApiKey)."
|
|
15943
16036
|
};
|
|
15944
16037
|
}
|
|
15945
|
-
return {
|
|
16038
|
+
return {
|
|
16039
|
+
ok: true,
|
|
16040
|
+
provider,
|
|
16041
|
+
model: provider === "openai" ? OPENAI_AI_MODEL : ANTHROPIC_AI_MODEL
|
|
16042
|
+
};
|
|
15946
16043
|
} catch (err) {
|
|
15947
16044
|
return { ok: false, error: err instanceof Error ? err.message : String(err) };
|
|
15948
16045
|
}
|