@ratio-mcp/dev-server 1.5.1 → 1.6.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/index.js +0 -0
- package/dist/schemas/app-distribution.json +36 -0
- package/dist/schemas/customers.json +26 -0
- package/dist/schemas/discounts.json +81 -0
- package/dist/schemas/draft_orders.json +112 -0
- package/dist/schemas/loyalty.json +147 -0
- package/dist/schemas/oauth.json +71 -0
- package/dist/schemas/orders.json +601 -106
- package/dist/schemas/product_reviews.json +45 -0
- package/dist/schemas/products.json +405 -122
- package/dist/schemas/tags.json +98 -0
- package/dist/schemas/variants.json +112 -0
- package/dist/schemas/webhooks.json +148 -0
- package/dist/server.d.ts.map +1 -1
- package/dist/server.js +39 -5
- package/dist/server.js.map +1 -1
- package/dist/services/app-state-sync.d.ts +34 -0
- package/dist/services/app-state-sync.d.ts.map +1 -0
- package/dist/services/app-state-sync.js +85 -0
- package/dist/services/app-state-sync.js.map +1 -0
- package/dist/services/elicitation.d.ts +25 -0
- package/dist/services/elicitation.d.ts.map +1 -0
- package/dist/services/elicitation.js +101 -0
- package/dist/services/elicitation.js.map +1 -0
- package/dist/services/flow-profiles.d.ts +46 -0
- package/dist/services/flow-profiles.d.ts.map +1 -0
- package/dist/services/flow-profiles.js +100 -0
- package/dist/services/flow-profiles.js.map +1 -0
- package/dist/services/questionnaire.d.ts +89 -0
- package/dist/services/questionnaire.d.ts.map +1 -0
- package/dist/services/questionnaire.js +167 -0
- package/dist/services/questionnaire.js.map +1 -0
- package/dist/services/session-state.d.ts +17 -0
- package/dist/services/session-state.d.ts.map +1 -1
- package/dist/services/session-state.js +26 -1
- package/dist/services/session-state.js.map +1 -1
- package/dist/services/tool-guard.d.ts +15 -18
- package/dist/services/tool-guard.d.ts.map +1 -1
- package/dist/services/tool-guard.js +150 -97
- package/dist/services/tool-guard.js.map +1 -1
- package/dist/tools/app-management.d.ts.map +1 -1
- package/dist/tools/app-management.js +181 -33
- package/dist/tools/app-management.js.map +1 -1
- package/dist/tools/gather-requirements.d.ts.map +1 -1
- package/dist/tools/gather-requirements.js +5 -3
- package/dist/tools/gather-requirements.js.map +1 -1
- package/dist/tools/get-status.d.ts.map +1 -1
- package/dist/tools/get-status.js +45 -34
- package/dist/tools/get-status.js.map +1 -1
- package/dist/tools/index.js +1 -1
- package/dist/tools/index.js.map +1 -1
- package/dist/tools/lifecycle.d.ts.map +1 -1
- package/dist/tools/lifecycle.js +6 -3
- package/dist/tools/lifecycle.js.map +1 -1
- package/dist/tools/reference-data.d.ts +16 -0
- package/dist/tools/reference-data.d.ts.map +1 -1
- package/dist/tools/reference-data.js +12 -8
- package/dist/tools/reference-data.js.map +1 -1
- package/dist/tools/requirements.d.ts.map +1 -1
- package/dist/tools/requirements.js +17 -9
- package/dist/tools/requirements.js.map +1 -1
- package/dist/tools/submission.d.ts.map +1 -1
- package/dist/tools/submission.js +50 -13
- package/dist/tools/submission.js.map +1 -1
- package/dist/tools/webhooks.d.ts.map +1 -1
- package/dist/tools/webhooks.js +3 -1
- package/dist/tools/webhooks.js.map +1 -1
- package/dist/utils/logger.d.ts +3 -2
- package/dist/utils/logger.d.ts.map +1 -1
- package/dist/utils/logger.js +19 -10
- package/dist/utils/logger.js.map +1 -1
- package/dist/utils/schema-resources.d.ts +8 -0
- package/dist/utils/schema-resources.d.ts.map +1 -0
- package/dist/utils/schema-resources.js +25 -0
- package/dist/utils/schema-resources.js.map +1 -0
- package/package.json +12 -13
|
@@ -0,0 +1,101 @@
|
|
|
1
|
+
import { logger } from '../utils/logger.js';
|
|
2
|
+
/**
|
|
3
|
+
* Elicitation — ask the HUMAN developer for input mid-tool-call.
|
|
4
|
+
*
|
|
5
|
+
* When the client supports MCP elicitation (spec 2025-06-18), the answer comes
|
|
6
|
+
* from a real form the user fills in — the LLM cannot fake it. When the client
|
|
7
|
+
* doesn't support it, callers fall back to the structured needs_input response
|
|
8
|
+
* (the LLM is instructed to ask, same as the legacy confirmation-boolean flow).
|
|
9
|
+
*
|
|
10
|
+
* The server reference is set once in createDevServer().
|
|
11
|
+
*/
|
|
12
|
+
let mcpServer;
|
|
13
|
+
// Some clients (e.g. Cowork) advertise the elicitation capability but immediately
|
|
14
|
+
// return a synthetic "cancel" without ever rendering the form. A server can't tell
|
|
15
|
+
// that apart from a real user decline, so after repeated consecutive declines we
|
|
16
|
+
// stop offering forms for the rest of the session and let callers use their chat
|
|
17
|
+
// fallback instead. A genuine user who declined twice prefers chat anyway.
|
|
18
|
+
const DECLINE_FALLBACK_THRESHOLD = 2;
|
|
19
|
+
let consecutiveDeclines = 0;
|
|
20
|
+
export function setElicitationServer(server) {
|
|
21
|
+
mcpServer = server;
|
|
22
|
+
}
|
|
23
|
+
export function isElicitationSupported() {
|
|
24
|
+
if (consecutiveDeclines >= DECLINE_FALLBACK_THRESHOLD)
|
|
25
|
+
return false;
|
|
26
|
+
try {
|
|
27
|
+
return Boolean(mcpServer?.server.getClientCapabilities()?.elicitation);
|
|
28
|
+
}
|
|
29
|
+
catch {
|
|
30
|
+
return false;
|
|
31
|
+
}
|
|
32
|
+
}
|
|
33
|
+
/**
|
|
34
|
+
* Ask the user to pick one option from a list. Returns 'unsupported' when the
|
|
35
|
+
* client can't render elicitation — the caller must then fall back to
|
|
36
|
+
* instructing the LLM to ask in chat.
|
|
37
|
+
*/
|
|
38
|
+
export async function elicitChoice(message, fieldName, fieldTitle, choices) {
|
|
39
|
+
if (!isElicitationSupported() || !mcpServer)
|
|
40
|
+
return { kind: 'unsupported' };
|
|
41
|
+
try {
|
|
42
|
+
const result = await mcpServer.server.elicitInput({
|
|
43
|
+
message,
|
|
44
|
+
requestedSchema: {
|
|
45
|
+
type: 'object',
|
|
46
|
+
properties: {
|
|
47
|
+
[fieldName]: {
|
|
48
|
+
type: 'string',
|
|
49
|
+
title: fieldTitle,
|
|
50
|
+
oneOf: choices.map((c) => ({
|
|
51
|
+
const: c.value,
|
|
52
|
+
title: c.description ? `${c.title} — ${c.description}` : c.title,
|
|
53
|
+
})),
|
|
54
|
+
},
|
|
55
|
+
},
|
|
56
|
+
required: [fieldName],
|
|
57
|
+
},
|
|
58
|
+
});
|
|
59
|
+
logger.info(`[elicitation] ${fieldName}: action=${result.action}, value=${JSON.stringify(result.content?.[fieldName])}`);
|
|
60
|
+
if (result.action === 'accept' && typeof result.content?.[fieldName] === 'string') {
|
|
61
|
+
consecutiveDeclines = 0;
|
|
62
|
+
return { kind: 'answered', value: result.content[fieldName] };
|
|
63
|
+
}
|
|
64
|
+
consecutiveDeclines++;
|
|
65
|
+
return { kind: 'declined' };
|
|
66
|
+
}
|
|
67
|
+
catch (err) {
|
|
68
|
+
// Client advertised the capability but failed to handle the request — fall back.
|
|
69
|
+
logger.warn(`[elicitation] elicitInput failed (${err instanceof Error ? err.message : err}) — falling back to chat`);
|
|
70
|
+
return { kind: 'unsupported' };
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
/** Ask the user for free text (e.g. comma-separated merchant IDs). */
|
|
74
|
+
export async function elicitText(message, fieldName, fieldTitle, description) {
|
|
75
|
+
if (!isElicitationSupported() || !mcpServer)
|
|
76
|
+
return { kind: 'unsupported' };
|
|
77
|
+
try {
|
|
78
|
+
const result = await mcpServer.server.elicitInput({
|
|
79
|
+
message,
|
|
80
|
+
requestedSchema: {
|
|
81
|
+
type: 'object',
|
|
82
|
+
properties: {
|
|
83
|
+
[fieldName]: { type: 'string', title: fieldTitle, description },
|
|
84
|
+
},
|
|
85
|
+
required: [fieldName],
|
|
86
|
+
},
|
|
87
|
+
});
|
|
88
|
+
logger.info(`[elicitation] ${fieldName}: action=${result.action}`);
|
|
89
|
+
if (result.action === 'accept' && typeof result.content?.[fieldName] === 'string') {
|
|
90
|
+
consecutiveDeclines = 0;
|
|
91
|
+
return { kind: 'answered', value: result.content[fieldName] };
|
|
92
|
+
}
|
|
93
|
+
consecutiveDeclines++;
|
|
94
|
+
return { kind: 'declined' };
|
|
95
|
+
}
|
|
96
|
+
catch (err) {
|
|
97
|
+
logger.warn(`[elicitation] elicitInput failed (${err instanceof Error ? err.message : err}) — falling back to chat`);
|
|
98
|
+
return { kind: 'unsupported' };
|
|
99
|
+
}
|
|
100
|
+
}
|
|
101
|
+
//# sourceMappingURL=elicitation.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"elicitation.js","sourceRoot":"","sources":["../../src/services/elicitation.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,MAAM,EAAE,MAAM,oBAAoB,CAAC;AAE5C;;;;;;;;;GASG;AAEH,IAAI,SAAgC,CAAC;AAErC,kFAAkF;AAClF,mFAAmF;AACnF,iFAAiF;AACjF,iFAAiF;AACjF,2EAA2E;AAC3E,MAAM,0BAA0B,GAAG,CAAC,CAAC;AACrC,IAAI,mBAAmB,GAAG,CAAC,CAAC;AAE5B,MAAM,UAAU,oBAAoB,CAAC,MAAiB;IACpD,SAAS,GAAG,MAAM,CAAC;AACrB,CAAC;AAED,MAAM,UAAU,sBAAsB;IACpC,IAAI,mBAAmB,IAAI,0BAA0B;QAAE,OAAO,KAAK,CAAC;IACpE,IAAI,CAAC;QACH,OAAO,OAAO,CAAC,SAAS,EAAE,MAAM,CAAC,qBAAqB,EAAE,EAAE,WAAW,CAAC,CAAC;IACzE,CAAC;IAAC,MAAM,CAAC;QACP,OAAO,KAAK,CAAC;IACf,CAAC;AACH,CAAC;AAaD;;;;GAIG;AACH,MAAM,CAAC,KAAK,UAAU,YAAY,CAChC,OAAe,EACf,SAAiB,EACjB,UAAkB,EAClB,OAAqB;IAErB,IAAI,CAAC,sBAAsB,EAAE,IAAI,CAAC,SAAS;QAAE,OAAO,EAAE,IAAI,EAAE,aAAa,EAAE,CAAC;IAE5E,IAAI,CAAC;QACH,MAAM,MAAM,GAAG,MAAM,SAAS,CAAC,MAAM,CAAC,WAAW,CAAC;YAChD,OAAO;YACP,eAAe,EAAE;gBACf,IAAI,EAAE,QAAQ;gBACd,UAAU,EAAE;oBACV,CAAC,SAAS,CAAC,EAAE;wBACX,IAAI,EAAE,QAAQ;wBACd,KAAK,EAAE,UAAU;wBACjB,KAAK,EAAE,OAAO,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC;4BACzB,KAAK,EAAE,CAAC,CAAC,KAAK;4BACd,KAAK,EAAE,CAAC,CAAC,WAAW,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,KAAK,MAAM,CAAC,CAAC,WAAW,EAAE,CAAC,CAAC,CAAC,CAAC,CAAC,KAAK;yBACjE,CAAC,CAAC;qBACJ;iBACF;gBACD,QAAQ,EAAE,CAAC,SAAS,CAAC;aACtB;SACF,CAAC,CAAC;QAEH,MAAM,CAAC,IAAI,CAAC,iBAAiB,SAAS,YAAY,MAAM,CAAC,MAAM,WAAW,IAAI,CAAC,SAAS,CAAC,MAAM,CAAC,OAAO,EAAE,CAAC,SAAS,CAAC,CAAC,EAAE,CAAC,CAAC;QAEzH,IAAI,MAAM,CAAC,MAAM,KAAK,QAAQ,IAAI,OAAO,MAAM,CAAC,OAAO,EAAE,CAAC,SAAS,CAAC,KAAK,QAAQ,EAAE,CAAC;YAClF,mBAAmB,GAAG,CAAC,CAAC;YACxB,OAAO,EAAE,IAAI,EAAE,UAAU,EAAE,KAAK,EAAE,MAAM,CAAC,OAAO,CAAC,SAAS,CAAW,EAAE,CAAC;QAC1E,CAAC;QACD,mBAAmB,EAAE,CAAC;QACtB,OAAO,EAAE,IAAI,EAAE,UAAU,EAAE,CAAC;IAC9B,CAAC;IAAC,OAAO,GAAG,EAAE,CAAC;QACb,iFAAiF;QACjF,MAAM,CAAC,IAAI,CAAC,qCAAqC,GAAG,YAAY,KAAK,CAAC,CAAC,CAAC,GAAG,CAAC,OAAO,CAAC,CAAC,CAAC,GAAG,0BAA0B,CAAC,CAAC;QACrH,OAAO,EAAE,IAAI,EAAE,aAAa,EAAE,CAAC;IACjC,CAAC;AACH,CAAC;AAED,sEAAsE;AACtE,MAAM,CAAC,KAAK,UAAU,UAAU,CAC9B,OAAe,EACf,SAAiB,EACjB,UAAkB,EAClB,WAAoB;IAEpB,IAAI,CAAC,sBAAsB,EAAE,IAAI,CAAC,SAAS;QAAE,OAAO,EAAE,IAAI,EAAE,aAAa,EAAE,CAAC;IAE5E,IAAI,CAAC;QACH,MAAM,MAAM,GAAG,MAAM,SAAS,CAAC,MAAM,CAAC,WAAW,CAAC;YAChD,OAAO;YACP,eAAe,EAAE;gBACf,IAAI,EAAE,QAAQ;gBACd,UAAU,EAAE;oBACV,CAAC,SAAS,CAAC,EAAE,EAAE,IAAI,EAAE,QAAQ,EAAE,KAAK,EAAE,UAAU,EAAE,WAAW,EAAE;iBAChE;gBACD,QAAQ,EAAE,CAAC,SAAS,CAAC;aACtB;SACF,CAAC,CAAC;QAEH,MAAM,CAAC,IAAI,CAAC,iBAAiB,SAAS,YAAY,MAAM,CAAC,MAAM,EAAE,CAAC,CAAC;QAEnE,IAAI,MAAM,CAAC,MAAM,KAAK,QAAQ,IAAI,OAAO,MAAM,CAAC,OAAO,EAAE,CAAC,SAAS,CAAC,KAAK,QAAQ,EAAE,CAAC;YAClF,mBAAmB,GAAG,CAAC,CAAC;YACxB,OAAO,EAAE,IAAI,EAAE,UAAU,EAAE,KAAK,EAAE,MAAM,CAAC,OAAO,CAAC,SAAS,CAAW,EAAE,CAAC;QAC1E,CAAC;QACD,mBAAmB,EAAE,CAAC;QACtB,OAAO,EAAE,IAAI,EAAE,UAAU,EAAE,CAAC;IAC9B,CAAC;IAAC,OAAO,GAAG,EAAE,CAAC;QACb,MAAM,CAAC,IAAI,CAAC,qCAAqC,GAAG,YAAY,KAAK,CAAC,CAAC,CAAC,GAAG,CAAC,OAAO,CAAC,CAAC,CAAC,GAAG,0BAA0B,CAAC,CAAC;QACrH,OAAO,EAAE,IAAI,EAAE,aAAa,EAAE,CAAC;IACjC,CAAC;AACH,CAAC"}
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
import type { AppKind, AppType } from '@ratio-mcp/shared';
|
|
2
|
+
/**
|
|
3
|
+
* Flow profiles — the app lifecycle pipeline as data.
|
|
4
|
+
*
|
|
5
|
+
* The step sequence depends on (app_type, app_kind):
|
|
6
|
+
* - private apps auto-publish on upload (PrivateAppAutoReviewService in the backend),
|
|
7
|
+
* so submit_for_review / get_app_status-wait / publish_app do not exist for them.
|
|
8
|
+
* - no_ui apps have no frontend, so the scaffold/build/zip steps do not exist for them,
|
|
9
|
+
* and upload_build is called WITHOUT a zip (backend creates the version file-less).
|
|
10
|
+
*
|
|
11
|
+
* Guards, get_status and tool next_steps all derive from these profiles so the
|
|
12
|
+
* sequence lives in exactly one place.
|
|
13
|
+
*/
|
|
14
|
+
/** Kinds the MCP can take end-to-end today. Others can be created but need the
|
|
15
|
+
* manifest/bundle toolchain (portal upload) until bring-your-own-manifest lands. */
|
|
16
|
+
export declare const FULLY_SUPPORTED_KINDS: ReadonlySet<AppKind>;
|
|
17
|
+
export type ProfileStep = 'scaffold_frontend' | 'build_frontend' | 'validate_build' | 'create_submission_zip' | 'upload_build' | 'submit_for_review' | 'publish_app' | 'scaffold_backend' | 'generate_api_routes';
|
|
18
|
+
export interface SkipInfo {
|
|
19
|
+
reason: string;
|
|
20
|
+
/** The step the caller should take instead */
|
|
21
|
+
next: ProfileStep;
|
|
22
|
+
}
|
|
23
|
+
export interface FlowProfile {
|
|
24
|
+
appType: AppType;
|
|
25
|
+
appKind: AppKind;
|
|
26
|
+
/** Ordered pipeline steps after define_app_requirements */
|
|
27
|
+
steps: ProfileStep[];
|
|
28
|
+
/** Steps that exist in other profiles but are NOT applicable here */
|
|
29
|
+
skipped: Partial<Record<ProfileStep, SkipInfo>>;
|
|
30
|
+
/** Whether upload_build needs a zip file (false → file-less upload, backend creates the version) */
|
|
31
|
+
uploadNeedsZip: boolean;
|
|
32
|
+
/** Human summary shown to the LLM/developer */
|
|
33
|
+
summary: string;
|
|
34
|
+
}
|
|
35
|
+
/**
|
|
36
|
+
* Resolve the flow profile for an app. Unknown/missing values fall back to
|
|
37
|
+
* public:embedded (the historical default) so legacy sessions keep working.
|
|
38
|
+
*/
|
|
39
|
+
export declare function getFlowProfile(appType?: string, appKind?: string): FlowProfile;
|
|
40
|
+
/** Whether the MCP can run this kind's pipeline end-to-end (vs create-only + portal upload) */
|
|
41
|
+
export declare function isKindFullySupported(appKind?: string): boolean;
|
|
42
|
+
/** Skip info (reason + next step) when the step is not applicable for this profile */
|
|
43
|
+
export declare function getSkipInfo(profile: FlowProfile, step: string): SkipInfo | undefined;
|
|
44
|
+
/** Profile-driven next_steps text for create_app / upload_build responses */
|
|
45
|
+
export declare function describePipeline(profile: FlowProfile): string[];
|
|
46
|
+
//# sourceMappingURL=flow-profiles.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"flow-profiles.d.ts","sourceRoot":"","sources":["../../src/services/flow-profiles.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,OAAO,EAAE,OAAO,EAAE,MAAM,mBAAmB,CAAC;AAE1D;;;;;;;;;;;GAWG;AAEH;qFACqF;AACrF,eAAO,MAAM,qBAAqB,EAAE,WAAW,CAAC,OAAO,CAAkC,CAAC;AAE1F,MAAM,MAAM,WAAW,GACnB,mBAAmB,GACnB,gBAAgB,GAChB,gBAAgB,GAChB,uBAAuB,GACvB,cAAc,GACd,mBAAmB,GACnB,aAAa,GACb,kBAAkB,GAClB,qBAAqB,CAAC;AAa1B,MAAM,WAAW,QAAQ;IACvB,MAAM,EAAE,MAAM,CAAC;IACf,8CAA8C;IAC9C,IAAI,EAAE,WAAW,CAAC;CACnB;AAED,MAAM,WAAW,WAAW;IAC1B,OAAO,EAAE,OAAO,CAAC;IACjB,OAAO,EAAE,OAAO,CAAC;IACjB,2DAA2D;IAC3D,KAAK,EAAE,WAAW,EAAE,CAAC;IACrB,qEAAqE;IACrE,OAAO,EAAE,OAAO,CAAC,MAAM,CAAC,WAAW,EAAE,QAAQ,CAAC,CAAC,CAAC;IAChD,oGAAoG;IACpG,cAAc,EAAE,OAAO,CAAC;IACxB,+CAA+C;IAC/C,OAAO,EAAE,MAAM,CAAC;CACjB;AA4CD;;;GAGG;AACH,wBAAgB,cAAc,CAAC,OAAO,CAAC,EAAE,MAAM,EAAE,OAAO,CAAC,EAAE,MAAM,GAAG,WAAW,CAI9E;AAED,+FAA+F;AAC/F,wBAAgB,oBAAoB,CAAC,OAAO,CAAC,EAAE,MAAM,GAAG,OAAO,CAE9D;AAED,sFAAsF;AACtF,wBAAgB,WAAW,CAAC,OAAO,EAAE,WAAW,EAAE,IAAI,EAAE,MAAM,GAAG,QAAQ,GAAG,SAAS,CAEpF;AAED,6EAA6E;AAC7E,wBAAgB,gBAAgB,CAAC,OAAO,EAAE,WAAW,GAAG,MAAM,EAAE,CAoB/D"}
|
|
@@ -0,0 +1,100 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Flow profiles — the app lifecycle pipeline as data.
|
|
3
|
+
*
|
|
4
|
+
* The step sequence depends on (app_type, app_kind):
|
|
5
|
+
* - private apps auto-publish on upload (PrivateAppAutoReviewService in the backend),
|
|
6
|
+
* so submit_for_review / get_app_status-wait / publish_app do not exist for them.
|
|
7
|
+
* - no_ui apps have no frontend, so the scaffold/build/zip steps do not exist for them,
|
|
8
|
+
* and upload_build is called WITHOUT a zip (backend creates the version file-less).
|
|
9
|
+
*
|
|
10
|
+
* Guards, get_status and tool next_steps all derive from these profiles so the
|
|
11
|
+
* sequence lives in exactly one place.
|
|
12
|
+
*/
|
|
13
|
+
/** Kinds the MCP can take end-to-end today. Others can be created but need the
|
|
14
|
+
* manifest/bundle toolchain (portal upload) until bring-your-own-manifest lands. */
|
|
15
|
+
export const FULLY_SUPPORTED_KINDS = new Set(['embedded', 'no_ui']);
|
|
16
|
+
const FRONTEND_STEPS = [
|
|
17
|
+
'scaffold_frontend',
|
|
18
|
+
'build_frontend',
|
|
19
|
+
'validate_build',
|
|
20
|
+
'create_submission_zip',
|
|
21
|
+
];
|
|
22
|
+
const REVIEW_STEPS = ['submit_for_review', 'publish_app'];
|
|
23
|
+
const BACKEND_STEPS = ['scaffold_backend', 'generate_api_routes'];
|
|
24
|
+
function buildProfile(appType, appKind) {
|
|
25
|
+
const hasFrontend = appKind === 'embedded';
|
|
26
|
+
const isPrivate = appType === 'private';
|
|
27
|
+
const steps = [
|
|
28
|
+
...(hasFrontend ? FRONTEND_STEPS : []),
|
|
29
|
+
'upload_build',
|
|
30
|
+
...(isPrivate ? [] : REVIEW_STEPS),
|
|
31
|
+
...BACKEND_STEPS,
|
|
32
|
+
];
|
|
33
|
+
const skipped = {};
|
|
34
|
+
if (!hasFrontend) {
|
|
35
|
+
const reason = `${appKind} apps have no frontend — there is nothing to scaffold, build or zip. upload_build creates the version directly.`;
|
|
36
|
+
for (const step of FRONTEND_STEPS)
|
|
37
|
+
skipped[step] = { reason, next: 'upload_build' };
|
|
38
|
+
}
|
|
39
|
+
if (isPrivate) {
|
|
40
|
+
const reason = 'Private apps auto-publish when the build is uploaded — there is no admin review or manual publish. ' +
|
|
41
|
+
'After upload_build the app is already published; continue with scaffold_backend.';
|
|
42
|
+
for (const step of REVIEW_STEPS)
|
|
43
|
+
skipped[step] = { reason, next: 'scaffold_backend' };
|
|
44
|
+
}
|
|
45
|
+
const summary = [
|
|
46
|
+
isPrivate
|
|
47
|
+
? 'PRIVATE app: auto-publishes on upload (no admin review, no publish step).'
|
|
48
|
+
: 'PUBLIC app: needs admin review after submission, then publish.',
|
|
49
|
+
hasFrontend
|
|
50
|
+
? 'EMBEDDED kind: frontend is scaffolded, built and zipped before upload.'
|
|
51
|
+
: `${appKind.toUpperCase()} kind: no frontend pipeline — upload creates the version directly.`,
|
|
52
|
+
].join(' ');
|
|
53
|
+
return { appType, appKind, steps, skipped, uploadNeedsZip: hasFrontend, summary };
|
|
54
|
+
}
|
|
55
|
+
const PROFILES = new Map();
|
|
56
|
+
for (const appType of ['public', 'private']) {
|
|
57
|
+
for (const appKind of ['embedded', 'no_ui']) {
|
|
58
|
+
PROFILES.set(`${appType}:${appKind}`, buildProfile(appType, appKind));
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
/**
|
|
62
|
+
* Resolve the flow profile for an app. Unknown/missing values fall back to
|
|
63
|
+
* public:embedded (the historical default) so legacy sessions keep working.
|
|
64
|
+
*/
|
|
65
|
+
export function getFlowProfile(appType, appKind) {
|
|
66
|
+
const type = appType === 'private' ? 'private' : 'public';
|
|
67
|
+
const kind = appKind === 'no_ui' ? 'no_ui' : 'embedded';
|
|
68
|
+
return PROFILES.get(`${type}:${kind}`);
|
|
69
|
+
}
|
|
70
|
+
/** Whether the MCP can run this kind's pipeline end-to-end (vs create-only + portal upload) */
|
|
71
|
+
export function isKindFullySupported(appKind) {
|
|
72
|
+
return FULLY_SUPPORTED_KINDS.has((appKind ?? 'embedded'));
|
|
73
|
+
}
|
|
74
|
+
/** Skip info (reason + next step) when the step is not applicable for this profile */
|
|
75
|
+
export function getSkipInfo(profile, step) {
|
|
76
|
+
return profile.skipped[step];
|
|
77
|
+
}
|
|
78
|
+
/** Profile-driven next_steps text for create_app / upload_build responses */
|
|
79
|
+
export function describePipeline(profile) {
|
|
80
|
+
const labels = {
|
|
81
|
+
scaffold_frontend: 'scaffold_frontend — create the frontend project',
|
|
82
|
+
build_frontend: 'build_frontend — production build',
|
|
83
|
+
validate_build: 'validate_build — check the build output',
|
|
84
|
+
create_submission_zip: 'create_submission_zip — package the build',
|
|
85
|
+
upload_build: profile.uploadNeedsZip
|
|
86
|
+
? 'upload_build — upload the zip (creates a new app version)'
|
|
87
|
+
: 'upload_build — creates the app version (no zip needed for this app kind)',
|
|
88
|
+
submit_for_review: 'submit_for_review — send to admin review',
|
|
89
|
+
publish_app: 'publish_app — publish the approved version',
|
|
90
|
+
scaffold_backend: 'scaffold_backend — NestJS backend with OAuth callback handler',
|
|
91
|
+
generate_api_routes: 'generate_api_routes — scope-filtered API routes (+ webhook handlers)',
|
|
92
|
+
};
|
|
93
|
+
const lines = profile.steps.map((s, i) => `${i + 1}. ${labels[s]}`);
|
|
94
|
+
if (profile.appType === 'private') {
|
|
95
|
+
const uploadIdx = profile.steps.indexOf('upload_build');
|
|
96
|
+
lines[uploadIdx] += ' → AUTO-PUBLISHES (private app)';
|
|
97
|
+
}
|
|
98
|
+
return lines;
|
|
99
|
+
}
|
|
100
|
+
//# sourceMappingURL=flow-profiles.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"flow-profiles.js","sourceRoot":"","sources":["../../src/services/flow-profiles.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,89 @@
|
|
|
1
|
+
import { type AppKind, type AppType } from '@ratio-mcp/shared';
|
|
2
|
+
import { type ElicitOutcome } from './elicitation.js';
|
|
3
|
+
/**
|
|
4
|
+
* Declarative configuration questionnaire for create_app.
|
|
5
|
+
*
|
|
6
|
+
* CONTRACT: the server derives outstanding questions from confirmed answers;
|
|
7
|
+
* LLM inferences are treated only as suggested initial values, never as
|
|
8
|
+
* confirmed input.
|
|
9
|
+
*
|
|
10
|
+
* Both collection mechanisms run the same question list:
|
|
11
|
+
* - elicitation (client renders a real form): collectViaElicitation() loops
|
|
12
|
+
* the outstanding questions inside a single create_app call — the human's
|
|
13
|
+
* form answer is the only thing that can confirm a value.
|
|
14
|
+
* - chat fallback (no forms): create_app returns the outstanding questions
|
|
15
|
+
* for the LLM to relay verbatim; a follow-up call delivers the developer's
|
|
16
|
+
* answers via applyRelayedAnswers().
|
|
17
|
+
*
|
|
18
|
+
* There is no stored workflow stage — everything is derived from the answers
|
|
19
|
+
* (outstanding() / stage()), so conditional questions (merchant_ids only for
|
|
20
|
+
* private apps) appear and disappear without transitions or cleanup.
|
|
21
|
+
*/
|
|
22
|
+
export interface AppConfigAnswers {
|
|
23
|
+
app_type?: AppType;
|
|
24
|
+
app_kind?: AppKind;
|
|
25
|
+
merchant_ids?: string[];
|
|
26
|
+
category_ids?: string[];
|
|
27
|
+
}
|
|
28
|
+
export type AnswerKey = keyof AppConfigAnswers;
|
|
29
|
+
export interface PendingAppDraft {
|
|
30
|
+
confirmedAnswers: AppConfigAnswers;
|
|
31
|
+
/** The assistant's guesses — surfaced to the developer as suggestions, never trusted. */
|
|
32
|
+
inferredAnswers: Partial<Record<AnswerKey, unknown>>;
|
|
33
|
+
}
|
|
34
|
+
type Normalized = {
|
|
35
|
+
ok: true;
|
|
36
|
+
value: AppConfigAnswers[AnswerKey];
|
|
37
|
+
} | {
|
|
38
|
+
ok: false;
|
|
39
|
+
error: string;
|
|
40
|
+
};
|
|
41
|
+
export interface AppConfigQuestion {
|
|
42
|
+
key: AnswerKey;
|
|
43
|
+
required: (answers: AppConfigAnswers) => boolean;
|
|
44
|
+
/** Validate + normalize a raw answer (form string or tool-arg value). */
|
|
45
|
+
normalize: (raw: unknown) => Promise<Normalized>;
|
|
46
|
+
/** Ask the human via an MCP elicitation form. */
|
|
47
|
+
elicit: (inferred: unknown, retryNote?: string) => Promise<ElicitOutcome<string>>;
|
|
48
|
+
/** Question spec for the chat fallback — relayed to the developer verbatim. */
|
|
49
|
+
describe: (inferred: unknown) => Record<string, unknown>;
|
|
50
|
+
}
|
|
51
|
+
export declare const APP_CONFIG_QUESTIONS: AppConfigQuestion[];
|
|
52
|
+
/** Questions that are required given the current answers and not yet answered. */
|
|
53
|
+
export declare function outstanding(answers: AppConfigAnswers): AppConfigQuestion[];
|
|
54
|
+
export declare function stage(answers: AppConfigAnswers): 'collecting_configuration' | 'ready';
|
|
55
|
+
export type CollectResult = {
|
|
56
|
+
status: 'complete';
|
|
57
|
+
} | {
|
|
58
|
+
status: 'declined';
|
|
59
|
+
key: AnswerKey;
|
|
60
|
+
} | {
|
|
61
|
+
status: 'invalid';
|
|
62
|
+
key: AnswerKey;
|
|
63
|
+
error: string;
|
|
64
|
+
} | {
|
|
65
|
+
status: 'unsupported';
|
|
66
|
+
};
|
|
67
|
+
/**
|
|
68
|
+
* Elicitation path: walk the outstanding questions with real forms, mutating
|
|
69
|
+
* draft.confirmedAnswers as the human answers. Runs unconditionally — values
|
|
70
|
+
* the LLM passed live only in draft.inferredAnswers and pre-seed the messages.
|
|
71
|
+
*/
|
|
72
|
+
export declare function collectViaElicitation(draft: PendingAppDraft): Promise<CollectResult>;
|
|
73
|
+
/**
|
|
74
|
+
* Chat-fallback path: accept the developer's relayed answers for outstanding
|
|
75
|
+
* questions. Applied in question order so conditional requirements see earlier
|
|
76
|
+
* answers (e.g. merchant_ids after app_type=private). Already-confirmed answers
|
|
77
|
+
* are never overwritten — a retry after a failed create must not let the LLM
|
|
78
|
+
* replace values the human confirmed via a form.
|
|
79
|
+
*/
|
|
80
|
+
export declare function applyRelayedAnswers(draft: PendingAppDraft, relayed: Partial<Record<AnswerKey, unknown>>): Promise<{
|
|
81
|
+
rejected: Array<{
|
|
82
|
+
key: AnswerKey;
|
|
83
|
+
error: string;
|
|
84
|
+
}>;
|
|
85
|
+
}>;
|
|
86
|
+
/** Chat-fallback question set for the LLM to relay to the developer verbatim. */
|
|
87
|
+
export declare function buildFallbackQuestions(draft: PendingAppDraft): Array<Record<string, unknown>>;
|
|
88
|
+
export {};
|
|
89
|
+
//# sourceMappingURL=questionnaire.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"questionnaire.d.ts","sourceRoot":"","sources":["../../src/services/questionnaire.ts"],"names":[],"mappings":"AAAA,OAAO,EAAoC,KAAK,OAAO,EAAE,KAAK,OAAO,EAAE,MAAM,mBAAmB,CAAC;AACjG,OAAO,EAA4B,KAAK,aAAa,EAAE,MAAM,kBAAkB,CAAC;AAGhF;;;;;;;;;;;;;;;;;;GAkBG;AAEH,MAAM,WAAW,gBAAgB;IAC/B,QAAQ,CAAC,EAAE,OAAO,CAAC;IACnB,QAAQ,CAAC,EAAE,OAAO,CAAC;IACnB,YAAY,CAAC,EAAE,MAAM,EAAE,CAAC;IACxB,YAAY,CAAC,EAAE,MAAM,EAAE,CAAC;CACzB;AAED,MAAM,MAAM,SAAS,GAAG,MAAM,gBAAgB,CAAC;AAE/C,MAAM,WAAW,eAAe;IAC9B,gBAAgB,EAAE,gBAAgB,CAAC;IACnC,yFAAyF;IACzF,eAAe,EAAE,OAAO,CAAC,MAAM,CAAC,SAAS,EAAE,OAAO,CAAC,CAAC,CAAC;CACtD;AAED,KAAK,UAAU,GACX;IAAE,EAAE,EAAE,IAAI,CAAC;IAAC,KAAK,EAAE,gBAAgB,CAAC,SAAS,CAAC,CAAA;CAAE,GAChD;IAAE,EAAE,EAAE,KAAK,CAAC;IAAC,KAAK,EAAE,MAAM,CAAA;CAAE,CAAC;AAEjC,MAAM,WAAW,iBAAiB;IAChC,GAAG,EAAE,SAAS,CAAC;IACf,QAAQ,EAAE,CAAC,OAAO,EAAE,gBAAgB,KAAK,OAAO,CAAC;IACjD,yEAAyE;IACzE,SAAS,EAAE,CAAC,GAAG,EAAE,OAAO,KAAK,OAAO,CAAC,UAAU,CAAC,CAAC;IACjD,iDAAiD;IACjD,MAAM,EAAE,CAAC,QAAQ,EAAE,OAAO,EAAE,SAAS,CAAC,EAAE,MAAM,KAAK,OAAO,CAAC,aAAa,CAAC,MAAM,CAAC,CAAC,CAAC;IAClF,+EAA+E;IAC/E,QAAQ,EAAE,CAAC,QAAQ,EAAE,OAAO,KAAK,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;CAC1D;AAeD,eAAO,MAAM,oBAAoB,EAAE,iBAAiB,EA2GnD,CAAC;AAEF,kFAAkF;AAClF,wBAAgB,WAAW,CAAC,OAAO,EAAE,gBAAgB,GAAG,iBAAiB,EAAE,CAE1E;AAED,wBAAgB,KAAK,CAAC,OAAO,EAAE,gBAAgB,GAAG,0BAA0B,GAAG,OAAO,CAErF;AAED,MAAM,MAAM,aAAa,GACrB;IAAE,MAAM,EAAE,UAAU,CAAA;CAAE,GACtB;IAAE,MAAM,EAAE,UAAU,CAAC;IAAC,GAAG,EAAE,SAAS,CAAA;CAAE,GACtC;IAAE,MAAM,EAAE,SAAS,CAAC;IAAC,GAAG,EAAE,SAAS,CAAC;IAAC,KAAK,EAAE,MAAM,CAAA;CAAE,GACpD;IAAE,MAAM,EAAE,aAAa,CAAA;CAAE,CAAC;AAI9B;;;;GAIG;AACH,wBAAsB,qBAAqB,CAAC,KAAK,EAAE,eAAe,GAAG,OAAO,CAAC,aAAa,CAAC,CAqB1F;AAED;;;;;;GAMG;AACH,wBAAsB,mBAAmB,CACvC,KAAK,EAAE,eAAe,EACtB,OAAO,EAAE,OAAO,CAAC,MAAM,CAAC,SAAS,EAAE,OAAO,CAAC,CAAC,GAC3C,OAAO,CAAC;IAAE,QAAQ,EAAE,KAAK,CAAC;QAAE,GAAG,EAAE,SAAS,CAAC;QAAC,KAAK,EAAE,MAAM,CAAA;KAAE,CAAC,CAAA;CAAE,CAAC,CAiBjE;AAED,iFAAiF;AACjF,wBAAgB,sBAAsB,CAAC,KAAK,EAAE,eAAe,GAAG,KAAK,CAAC,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC,CAE7F"}
|
|
@@ -0,0 +1,167 @@
|
|
|
1
|
+
import { APP_KINDS, APP_KIND_DESCRIPTIONS } from '@ratio-mcp/shared';
|
|
2
|
+
import { elicitChoice, elicitText } from './elicitation.js';
|
|
3
|
+
import { listCategories } from '../tools/reference-data.js';
|
|
4
|
+
function inferredNote(inferred) {
|
|
5
|
+
if (inferred === undefined || inferred === null)
|
|
6
|
+
return '';
|
|
7
|
+
const shown = Array.isArray(inferred) ? inferred.join(', ') : String(inferred);
|
|
8
|
+
return ` The assistant inferred "${shown}" — confirm or change.`;
|
|
9
|
+
}
|
|
10
|
+
function toStringArray(raw) {
|
|
11
|
+
const parts = Array.isArray(raw) ? raw.map(String) : typeof raw === 'string' ? raw.split(',') : [];
|
|
12
|
+
return parts.map((p) => p.trim()).filter(Boolean);
|
|
13
|
+
}
|
|
14
|
+
const UUID_REGEX = /^[0-9a-f]{8}-[0-9a-f]{4}-4[0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$/i;
|
|
15
|
+
export const APP_CONFIG_QUESTIONS = [
|
|
16
|
+
{
|
|
17
|
+
key: 'app_type',
|
|
18
|
+
required: () => true,
|
|
19
|
+
normalize: async (raw) => {
|
|
20
|
+
const value = typeof raw === 'string' ? raw.trim().toLowerCase() : '';
|
|
21
|
+
if (value === 'public' || value === 'private')
|
|
22
|
+
return { ok: true, value };
|
|
23
|
+
return { ok: false, error: 'app_type must be "public" or "private".' };
|
|
24
|
+
},
|
|
25
|
+
elicit: (inferred, retryNote) => elicitChoice(`${retryNote ?? ''}How should this app be distributed?${inferredNote(inferred)}`, 'app_type', 'Distribution type', [
|
|
26
|
+
{ value: 'public', title: 'Public', description: 'listed in the marketplace, any merchant can install, requires admin review' },
|
|
27
|
+
{ value: 'private', title: 'Private', description: 'only whitelisted merchants, auto-publishes on build upload, not listed in the marketplace' },
|
|
28
|
+
]),
|
|
29
|
+
describe: (inferred) => ({
|
|
30
|
+
key: 'app_type',
|
|
31
|
+
question: 'Should this be a PUBLIC app (listed in the marketplace, any merchant can install, requires admin review) or a PRIVATE app (installable only by whitelisted merchants, auto-published on build upload, not in the marketplace)?',
|
|
32
|
+
options: ['public', 'private'],
|
|
33
|
+
...(inferred !== undefined ? { assistant_inferred: inferred } : {}),
|
|
34
|
+
}),
|
|
35
|
+
},
|
|
36
|
+
{
|
|
37
|
+
key: 'app_kind',
|
|
38
|
+
required: () => true,
|
|
39
|
+
normalize: async (raw) => {
|
|
40
|
+
const value = typeof raw === 'string' ? raw.trim().toLowerCase() : '';
|
|
41
|
+
if (APP_KINDS.includes(value))
|
|
42
|
+
return { ok: true, value: value };
|
|
43
|
+
return { ok: false, error: `app_kind must be one of: ${APP_KINDS.join(', ')}.` };
|
|
44
|
+
},
|
|
45
|
+
elicit: (inferred, retryNote) => elicitChoice(`${retryNote ?? ''}How will this app integrate with the platform?${inferredNote(inferred)}`, 'app_kind', 'App kind', APP_KINDS.map((kind) => ({ value: kind, title: kind, description: APP_KIND_DESCRIPTIONS[kind] }))),
|
|
46
|
+
describe: (inferred) => ({
|
|
47
|
+
key: 'app_kind',
|
|
48
|
+
question: 'Which kind of app are you building?',
|
|
49
|
+
options: APP_KINDS.map((k) => `"${k}" — ${APP_KIND_DESCRIPTIONS[k]}`),
|
|
50
|
+
...(inferred !== undefined ? { assistant_inferred: inferred } : {}),
|
|
51
|
+
}),
|
|
52
|
+
},
|
|
53
|
+
{
|
|
54
|
+
key: 'merchant_ids',
|
|
55
|
+
required: (answers) => answers.app_type === 'private',
|
|
56
|
+
normalize: async (raw) => {
|
|
57
|
+
const ids = toStringArray(raw);
|
|
58
|
+
if (ids.length > 0)
|
|
59
|
+
return { ok: true, value: ids };
|
|
60
|
+
return { ok: false, error: 'At least one merchant ID is required for private apps.' };
|
|
61
|
+
},
|
|
62
|
+
elicit: (inferred, retryNote) => elicitText(`${retryNote ?? ''}Private apps are installable only by whitelisted merchants. Which merchant IDs should be allowed to install this app?${inferredNote(inferred)}`, 'merchant_ids', 'Merchant IDs', 'Comma-separated merchant IDs (at least one)'),
|
|
63
|
+
describe: (inferred) => ({
|
|
64
|
+
key: 'merchant_ids',
|
|
65
|
+
question: 'Which merchant IDs should be whitelisted to install this private app? (at least one)',
|
|
66
|
+
format: 'array of merchant ID strings',
|
|
67
|
+
...(inferred !== undefined ? { assistant_inferred: inferred } : {}),
|
|
68
|
+
}),
|
|
69
|
+
},
|
|
70
|
+
{
|
|
71
|
+
key: 'category_ids',
|
|
72
|
+
required: () => true,
|
|
73
|
+
normalize: async (raw) => {
|
|
74
|
+
const slugs = toStringArray(raw);
|
|
75
|
+
if (slugs.length === 0)
|
|
76
|
+
return { ok: false, error: 'At least one category is required.' };
|
|
77
|
+
const categories = await listCategories();
|
|
78
|
+
const unknown = slugs.filter((s) => !UUID_REGEX.test(s) &&
|
|
79
|
+
!categories.some((c) => c.slug.toLowerCase() === s.toLowerCase() || c.name.toLowerCase() === s.toLowerCase()));
|
|
80
|
+
if (unknown.length > 0) {
|
|
81
|
+
return {
|
|
82
|
+
ok: false,
|
|
83
|
+
error: `Unknown categories: [${unknown.join(', ')}]. Valid slugs: ${categories.map((c) => c.slug).join(', ')}.`,
|
|
84
|
+
};
|
|
85
|
+
}
|
|
86
|
+
return { ok: true, value: slugs };
|
|
87
|
+
},
|
|
88
|
+
elicit: async (inferred, retryNote) => {
|
|
89
|
+
const categories = await listCategories();
|
|
90
|
+
const catalog = categories.map((c) => c.slug).join(', ');
|
|
91
|
+
return elicitText(`${retryNote ?? ''}Which categories fit this app? Available: ${catalog}.${inferredNote(inferred)}`, 'category_ids', 'Categories', 'Comma-separated category slugs (at least one)');
|
|
92
|
+
},
|
|
93
|
+
describe: (inferred) => ({
|
|
94
|
+
key: 'category_ids',
|
|
95
|
+
question: 'Which categories fit this app? Show the developer the list from get_categories and let them choose.',
|
|
96
|
+
format: 'array of category slugs from get_categories',
|
|
97
|
+
...(inferred !== undefined ? { assistant_inferred: inferred } : {}),
|
|
98
|
+
}),
|
|
99
|
+
},
|
|
100
|
+
];
|
|
101
|
+
/** Questions that are required given the current answers and not yet answered. */
|
|
102
|
+
export function outstanding(answers) {
|
|
103
|
+
return APP_CONFIG_QUESTIONS.filter((q) => q.required(answers) && answers[q.key] === undefined);
|
|
104
|
+
}
|
|
105
|
+
export function stage(answers) {
|
|
106
|
+
return outstanding(answers).length > 0 ? 'collecting_configuration' : 'ready';
|
|
107
|
+
}
|
|
108
|
+
const MAX_FORM_ATTEMPTS = 2;
|
|
109
|
+
/**
|
|
110
|
+
* Elicitation path: walk the outstanding questions with real forms, mutating
|
|
111
|
+
* draft.confirmedAnswers as the human answers. Runs unconditionally — values
|
|
112
|
+
* the LLM passed live only in draft.inferredAnswers and pre-seed the messages.
|
|
113
|
+
*/
|
|
114
|
+
export async function collectViaElicitation(draft) {
|
|
115
|
+
for (;;) {
|
|
116
|
+
const question = outstanding(draft.confirmedAnswers)[0];
|
|
117
|
+
if (!question)
|
|
118
|
+
return { status: 'complete' };
|
|
119
|
+
let retryNote;
|
|
120
|
+
let attempt = 0;
|
|
121
|
+
for (;;) {
|
|
122
|
+
const outcome = await question.elicit(draft.inferredAnswers[question.key], retryNote);
|
|
123
|
+
if (outcome.kind === 'unsupported')
|
|
124
|
+
return { status: 'unsupported' };
|
|
125
|
+
if (outcome.kind === 'declined')
|
|
126
|
+
return { status: 'declined', key: question.key };
|
|
127
|
+
const normalized = await question.normalize(outcome.value);
|
|
128
|
+
if (normalized.ok) {
|
|
129
|
+
draft.confirmedAnswers[question.key] = normalized.value;
|
|
130
|
+
break;
|
|
131
|
+
}
|
|
132
|
+
if (++attempt >= MAX_FORM_ATTEMPTS)
|
|
133
|
+
return { status: 'invalid', key: question.key, error: normalized.error };
|
|
134
|
+
retryNote = `${normalized.error} `;
|
|
135
|
+
}
|
|
136
|
+
}
|
|
137
|
+
}
|
|
138
|
+
/**
|
|
139
|
+
* Chat-fallback path: accept the developer's relayed answers for outstanding
|
|
140
|
+
* questions. Applied in question order so conditional requirements see earlier
|
|
141
|
+
* answers (e.g. merchant_ids after app_type=private). Already-confirmed answers
|
|
142
|
+
* are never overwritten — a retry after a failed create must not let the LLM
|
|
143
|
+
* replace values the human confirmed via a form.
|
|
144
|
+
*/
|
|
145
|
+
export async function applyRelayedAnswers(draft, relayed) {
|
|
146
|
+
const rejected = [];
|
|
147
|
+
for (const question of APP_CONFIG_QUESTIONS) {
|
|
148
|
+
const raw = relayed[question.key];
|
|
149
|
+
if (raw === undefined ||
|
|
150
|
+
draft.confirmedAnswers[question.key] !== undefined ||
|
|
151
|
+
!question.required(draft.confirmedAnswers))
|
|
152
|
+
continue;
|
|
153
|
+
const normalized = await question.normalize(raw);
|
|
154
|
+
if (normalized.ok) {
|
|
155
|
+
draft.confirmedAnswers[question.key] = normalized.value;
|
|
156
|
+
}
|
|
157
|
+
else {
|
|
158
|
+
rejected.push({ key: question.key, error: normalized.error });
|
|
159
|
+
}
|
|
160
|
+
}
|
|
161
|
+
return { rejected };
|
|
162
|
+
}
|
|
163
|
+
/** Chat-fallback question set for the LLM to relay to the developer verbatim. */
|
|
164
|
+
export function buildFallbackQuestions(draft) {
|
|
165
|
+
return outstanding(draft.confirmedAnswers).map((q) => q.describe(draft.inferredAnswers[q.key]));
|
|
166
|
+
}
|
|
167
|
+
//# sourceMappingURL=questionnaire.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"questionnaire.js","sourceRoot":"","sources":["../../src/services/questionnaire.ts"],"names":[],"mappings":"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"}
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import type { PendingAppDraft } from './questionnaire.js';
|
|
1
2
|
/**
|
|
2
3
|
* In-memory session state tracker (per-app).
|
|
3
4
|
*
|
|
@@ -14,6 +15,10 @@ export interface AppState {
|
|
|
14
15
|
appName?: string;
|
|
15
16
|
clientId?: string;
|
|
16
17
|
clientSecret?: string;
|
|
18
|
+
/** Distribution type — 'public' | 'private' (from create_app / platform) */
|
|
19
|
+
appType?: string;
|
|
20
|
+
/** Integration kind — 'embedded' | 'extension' | 'no_ui' | 'web_pixel' | 'flow' */
|
|
21
|
+
appKind?: string;
|
|
17
22
|
confirmedScopes?: string[];
|
|
18
23
|
scopesConfirmed?: boolean;
|
|
19
24
|
webhooksConfigured?: boolean;
|
|
@@ -63,6 +68,13 @@ export interface SessionState {
|
|
|
63
68
|
} & AppState['context'];
|
|
64
69
|
/** Per-app state, keyed by app ID */
|
|
65
70
|
apps: Record<string, AppState>;
|
|
71
|
+
/**
|
|
72
|
+
* Configuration answers being collected for an app that doesn't exist yet
|
|
73
|
+
* (create_app wizard). Session-level because there is no app_id to key by;
|
|
74
|
+
* one creation in flight at a time — a new creation replaces the draft.
|
|
75
|
+
* Deleted once the app is created (answers land in the requirements store).
|
|
76
|
+
*/
|
|
77
|
+
pendingAppDraft?: PendingAppDraft;
|
|
66
78
|
/** Timestamp of last tool call */
|
|
67
79
|
lastActivity: number;
|
|
68
80
|
}
|
|
@@ -70,12 +82,17 @@ export interface SessionState {
|
|
|
70
82
|
export declare function getSession(): SessionState;
|
|
71
83
|
/** Get state for a specific app */
|
|
72
84
|
export declare function getAppState(appId: string): AppState | undefined;
|
|
85
|
+
export declare function getPendingAppDraft(): PendingAppDraft | undefined;
|
|
86
|
+
export declare function setPendingAppDraft(draft: PendingAppDraft): void;
|
|
87
|
+
export declare function clearPendingAppDraft(): void;
|
|
73
88
|
/** Get the currently active app ID */
|
|
74
89
|
export declare function getActiveAppId(): string | undefined;
|
|
75
90
|
/** Set the active app ID */
|
|
76
91
|
export declare function setActiveAppId(appId: string): void;
|
|
77
92
|
/** Record that a tool completed successfully and extract relevant context */
|
|
78
93
|
export declare function recordToolSuccess(toolName: string, result: unknown): void;
|
|
94
|
+
/** Merge context values into an app's state (and the flattened global context if active) */
|
|
95
|
+
export declare function updateAppContext(appId: string, updates: Partial<AppState['context']>): void;
|
|
79
96
|
/** Reset session (for testing or starting a new flow) */
|
|
80
97
|
export declare function resetSession(): void;
|
|
81
98
|
/** Check if a specific tool has been completed (global or active app) */
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"session-state.d.ts","sourceRoot":"","sources":["../../src/services/session-state.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"session-state.d.ts","sourceRoot":"","sources":["../../src/services/session-state.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,oBAAoB,CAAC;AAE1D;;;;;;;;GAQG;AAIH,8EAA8E;AAC9E,MAAM,WAAW,QAAQ;IACvB,cAAc,EAAE,GAAG,CAAC,MAAM,CAAC,CAAC;IAC5B,OAAO,EAAE;QACP,OAAO,CAAC,EAAE,MAAM,CAAC;QACjB,QAAQ,CAAC,EAAE,MAAM,CAAC;QAClB,YAAY,CAAC,EAAE,MAAM,CAAC;QACtB,4EAA4E;QAC5E,OAAO,CAAC,EAAE,MAAM,CAAC;QACjB,mFAAmF;QACnF,OAAO,CAAC,EAAE,MAAM,CAAC;QACjB,eAAe,CAAC,EAAE,MAAM,EAAE,CAAC;QAC3B,eAAe,CAAC,EAAE,OAAO,CAAC;QAC1B,kBAAkB,CAAC,EAAE,OAAO,CAAC;QAC7B,UAAU,CAAC,EAAE,MAAM,EAAE,CAAC;QACtB,aAAa,CAAC,EAAE,OAAO,CAAC;QACxB,kBAAkB,CAAC,EAAE,OAAO,CAAC;QAC7B,WAAW,CAAC,EAAE,OAAO,CAAC;QACtB,YAAY,CAAC,EAAE,OAAO,CAAC;QACvB,cAAc,CAAC,EAAE,OAAO,CAAC;QACzB,QAAQ,CAAC,EAAE,MAAM,CAAC;QAClB,mBAAmB,CAAC,EAAE,MAAM,CAAC;QAC7B,cAAc,CAAC,EAAE,MAAM,CAAC;QACxB,WAAW,CAAC,EAAE,MAAM,CAAC;QACrB,2CAA2C;QAC3C,WAAW,CAAC,EAAE,MAAM,CAAC;QACrB,uEAAuE;QACvE,kBAAkB,CAAC,EAAE,MAAM,CAAC;QAC5B,qFAAqF;QACrF,WAAW,CAAC,EAAE,MAAM,CAAC;QACrB,4EAA4E;QAC5E,cAAc,CAAC,EAAE,OAAO,CAAC;QACzB,+EAA+E;QAC/E,iBAAiB,CAAC,EAAE,OAAO,CAAC;QAC5B,8EAA8E;QAC9E,mBAAmB,CAAC,EAAE,OAAO,CAAC;QAC9B,yCAAyC;QACzC,mBAAmB,CAAC,EAAE,MAAM,EAAE,CAAC;QAC/B,SAAS,CAAC,EAAE,MAAM,CAAC;QACnB,OAAO,CAAC,EAAE,MAAM,CAAC;QACjB,eAAe,CAAC,EAAE,MAAM,CAAC;KAC1B,CAAC;CACH;AAED,iDAAiD;AACjD,MAAM,WAAW,YAAY;IAC3B,4DAA4D;IAC5D,cAAc,EAAE,GAAG,CAAC,MAAM,CAAC,CAAC;IAE5B,iFAAiF;IACjF,OAAO,EAAE;QACP,QAAQ,CAAC,EAAE,OAAO,GAAG,QAAQ,CAAC;QAC9B,WAAW,CAAC,EAAE,MAAM,CAAC;QACrB,WAAW,CAAC,EAAE,MAAM,CAAC;QACrB,cAAc,CAAC,EAAE,MAAM,CAAC;QACxB,4EAA4E;QAC5E,WAAW,CAAC,EAAE,MAAM,CAAC;QACrB,8DAA8D;QAC9D,KAAK,CAAC,EAAE,MAAM,CAAC;KAChB,GAAG,QAAQ,CAAC,SAAS,CAAC,CAAC;IAExB,qCAAqC;IACrC,IAAI,EAAE,MAAM,CAAC,MAAM,EAAE,QAAQ,CAAC,CAAC;IAE/B;;;;;OAKG;IACH,eAAe,CAAC,EAAE,eAAe,CAAC;IAElC,kCAAkC;IAClC,YAAY,EAAE,MAAM,CAAC;CACtB;AA4BD,sDAAsD;AACtD,wBAAgB,UAAU,IAAI,YAAY,CAEzC;AAED,mCAAmC;AACnC,wBAAgB,WAAW,CAAC,KAAK,EAAE,MAAM,GAAG,QAAQ,GAAG,SAAS,CAE/D;AAED,wBAAgB,kBAAkB,IAAI,eAAe,GAAG,SAAS,CAEhE;AAED,wBAAgB,kBAAkB,CAAC,KAAK,EAAE,eAAe,GAAG,IAAI,CAE/D;AAED,wBAAgB,oBAAoB,IAAI,IAAI,CAE3C;AAED,sCAAsC;AACtC,wBAAgB,cAAc,IAAI,MAAM,GAAG,SAAS,CAEnD;AAED,4BAA4B;AAC5B,wBAAgB,cAAc,CAAC,KAAK,EAAE,MAAM,GAAG,IAAI,CAElD;AAED,6EAA6E;AAC7E,wBAAgB,iBAAiB,CAAC,QAAQ,EAAE,MAAM,EAAE,MAAM,EAAE,OAAO,GAAG,IAAI,CAqPzE;AAED,4FAA4F;AAC5F,wBAAgB,gBAAgB,CAAC,KAAK,EAAE,MAAM,EAAE,OAAO,EAAE,OAAO,CAAC,QAAQ,CAAC,SAAS,CAAC,CAAC,GAAG,IAAI,CAM3F;AA0BD,yDAAyD;AACzD,wBAAgB,YAAY,IAAI,IAAI,CAGnC;AAED,yEAAyE;AACzE,wBAAgB,YAAY,CAAC,QAAQ,EAAE,MAAM,GAAG,OAAO,CAOtD;AAED,yDAAyD;AACzD,wBAAgB,eAAe,CAAC,SAAS,EAAE,MAAM,EAAE,GAAG,OAAO,CAE5D;AAED,oDAAoD;AACpD,wBAAgB,oBAAoB,IAAI,MAAM,EAAE,CAS/C"}
|