@flytedesk/app-kit 5.0.0 → 6.0.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/auth/plugin.js +22 -2
- package/dist/auth/plugin.js.map +1 -1
- package/dist/auth/testing/fake-idp.d.ts +9 -0
- package/dist/auth/testing/fake-idp.js +33 -5
- package/dist/auth/testing/fake-idp.js.map +1 -1
- package/dist/bigquery/errors.d.ts +19 -0
- package/dist/bigquery/errors.js +41 -0
- package/dist/bigquery/errors.js.map +1 -1
- package/dist/bigquery/index.d.ts +2 -2
- package/dist/bigquery/index.js +1 -1
- package/dist/bigquery/index.js.map +1 -1
- package/dist/bigquery/query.js +14 -1
- package/dist/bigquery/query.js.map +1 -1
- package/dist/bigquery/schema.d.ts +5 -0
- package/dist/bigquery/schema.js +35 -0
- package/dist/bigquery/schema.js.map +1 -0
- package/dist/bigquery/types.d.ts +47 -2
- package/dist/chat/index.d.ts +52 -21
- package/dist/chat/index.js +50 -20
- package/dist/chat/index.js.map +1 -1
- package/dist/chat/mcp/entry.d.ts +37 -0
- package/dist/chat/mcp/entry.js +135 -0
- package/dist/chat/mcp/entry.js.map +1 -0
- package/dist/cli/is-running-as-main.d.ts +1 -0
- package/dist/cli/is-running-as-main.js +54 -0
- package/dist/cli/is-running-as-main.js.map +1 -0
- package/dist/cli/sync-engine.d.ts +1 -32
- package/dist/cli/sync-engine.js +7 -45
- package/dist/cli/sync-engine.js.map +1 -1
- package/dist/flags/evaluate.d.ts +43 -0
- package/dist/flags/evaluate.js +64 -0
- package/dist/flags/evaluate.js.map +1 -0
- package/dist/flags/index.d.ts +22 -6
- package/dist/flags/index.js +21 -6
- package/dist/flags/index.js.map +1 -1
- package/dist/flags/plugin.js +73 -7
- package/dist/flags/plugin.js.map +1 -1
- package/dist/flags/types.d.ts +25 -0
- package/package.json +3 -2
package/dist/bigquery/types.d.ts
CHANGED
|
@@ -32,6 +32,20 @@ export interface BigQueryTableReference {
|
|
|
32
32
|
datasetId?: string;
|
|
33
33
|
tableId?: string;
|
|
34
34
|
}
|
|
35
|
+
/** A result-schema field exactly as BigQuery's job statistics report it: the
|
|
36
|
+
* real SDK's `ITableFieldSchema`, where every property is optional. Only the
|
|
37
|
+
* raw boundary speaks this shape; callers get `BigQueryResultSchemaField`
|
|
38
|
+
* (name and type guaranteed) from `estimateQueryBytes`. */
|
|
39
|
+
export interface BigQueryRawSchemaField {
|
|
40
|
+
name?: string;
|
|
41
|
+
type?: string;
|
|
42
|
+
mode?: string;
|
|
43
|
+
fields?: BigQueryRawSchemaField[];
|
|
44
|
+
}
|
|
45
|
+
/** `IJobStatistics2.schema`, an `ITableSchema`. */
|
|
46
|
+
export interface BigQueryRawSchema {
|
|
47
|
+
fields?: BigQueryRawSchemaField[];
|
|
48
|
+
}
|
|
35
49
|
export interface BigQueryJobStatistics {
|
|
36
50
|
query?: {
|
|
37
51
|
totalBytesProcessed?: string | number;
|
|
@@ -42,6 +56,9 @@ export interface BigQueryJobStatistics {
|
|
|
42
56
|
statementType?: string;
|
|
43
57
|
/** Every table the query touches — `IJobStatistics2.referencedTables`. */
|
|
44
58
|
referencedTables?: BigQueryTableReference[];
|
|
59
|
+
/** The result schema BigQuery computed for the (dry-run) query —
|
|
60
|
+
* `IJobStatistics2.schema`, an `ITableSchema` (`{ fields }`). */
|
|
61
|
+
schema?: BigQueryRawSchema;
|
|
45
62
|
};
|
|
46
63
|
}
|
|
47
64
|
export interface BigQueryJobMetadata {
|
|
@@ -92,6 +109,10 @@ export interface BigQueryCreateQueryJobOptions {
|
|
|
92
109
|
* than run. Distinct from `EstimateQueryBytesOptions.maxBytesBilled`
|
|
93
110
|
* (a pre-flight dry-run check this module enforces client-side). */
|
|
94
111
|
maximumBytesBilled?: string;
|
|
112
|
+
/** BigQuery's own `configuration.jobTimeoutMs` — the job fails with
|
|
113
|
+
* `stopped` state once it runs longer than this, wall-clock, from job
|
|
114
|
+
* creation. The SDK converts this number to the wire's decimal string. */
|
|
115
|
+
jobTimeoutMs?: number;
|
|
95
116
|
}
|
|
96
117
|
export interface BigQueryExtractOptions {
|
|
97
118
|
/** `@google-cloud/bigquery`'s own extract-job vocabulary — note this is
|
|
@@ -111,14 +132,25 @@ export interface GcsFileLike {
|
|
|
111
132
|
};
|
|
112
133
|
}
|
|
113
134
|
/** One BigQuery table-schema field, as `createWriteStream`'s `schema` option
|
|
114
|
-
* (and the REST API's `tables.insert`) accepts it
|
|
115
|
-
* RECORD/STRUCT
|
|
135
|
+
* (and the REST API's `tables.insert`) accepts it: load-job INPUT, where the
|
|
136
|
+
* caller must state `name` and `type`. `fields` recurses for RECORD/STRUCT
|
|
137
|
+
* columns. The schema BigQuery reports back is `BigQueryResultSchemaField`. */
|
|
116
138
|
export interface BigQuerySchemaField {
|
|
117
139
|
name: string;
|
|
118
140
|
type: string;
|
|
119
141
|
mode?: "NULLABLE" | "REQUIRED" | "REPEATED";
|
|
120
142
|
fields?: BigQuerySchemaField[];
|
|
121
143
|
}
|
|
144
|
+
/** One column of a query's result schema, normalised from BigQuery's own
|
|
145
|
+
* dry-run statistics: `name` and `type` are guaranteed (a field missing
|
|
146
|
+
* either is rejected with `MalformedResultSchemaError`); `mode` is BigQuery's
|
|
147
|
+
* string as reported. `fields` recurses for RECORD/STRUCT columns. */
|
|
148
|
+
export interface BigQueryResultSchemaField {
|
|
149
|
+
name: string;
|
|
150
|
+
type: string;
|
|
151
|
+
mode?: string;
|
|
152
|
+
fields?: BigQueryResultSchemaField[];
|
|
153
|
+
}
|
|
122
154
|
export interface BigQueryLoadOptions {
|
|
123
155
|
sourceFormat?: "NEWLINE_DELIMITED_JSON" | "CSV" | "AVRO" | "PARQUET";
|
|
124
156
|
/** Full destination-table schema. Required when the table doesn't already
|
|
@@ -182,6 +214,11 @@ export interface RunQueryOptions {
|
|
|
182
214
|
* belt to that check's suspenders, since data can grow between a dry
|
|
183
215
|
* run and the real job, or a caller can skip the dry run entirely. */
|
|
184
216
|
maximumBytesBilled?: number;
|
|
217
|
+
/** Fails the job with a typed `JobTimeoutError` once it runs longer than
|
|
218
|
+
* this many milliseconds, wall-clock from job creation — set on the job's
|
|
219
|
+
* own `configuration.jobTimeoutMs` (BigQuery enforces it server-side),
|
|
220
|
+
* not a client-side `Promise.race`. */
|
|
221
|
+
jobTimeoutMs?: number;
|
|
185
222
|
}
|
|
186
223
|
export interface QueryPage<T = Record<string, unknown>> {
|
|
187
224
|
rows: T[];
|
|
@@ -197,6 +234,10 @@ export interface EstimateQueryBytesOptions {
|
|
|
197
234
|
/** When given, `estimateQueryBytes` throws `DryRunBudgetExceededError`
|
|
198
235
|
* instead of returning once the estimate exceeds this many bytes. */
|
|
199
236
|
maxBytesBilled?: number;
|
|
237
|
+
/** Same `configuration.jobTimeoutMs` enforcement as `RunQueryOptions` —
|
|
238
|
+
* applies to the dry-run job itself, not the (never-run) query it
|
|
239
|
+
* estimates. */
|
|
240
|
+
jobTimeoutMs?: number;
|
|
200
241
|
}
|
|
201
242
|
export interface DryRunEstimate {
|
|
202
243
|
totalBytesProcessed: number;
|
|
@@ -211,6 +252,10 @@ export interface DryRunEstimate {
|
|
|
211
252
|
* checks every entry against its own read-only table allowlist before
|
|
212
253
|
* ever submitting the real job. */
|
|
213
254
|
referencedTables?: BigQueryTableReference[];
|
|
255
|
+
/** BigQuery's own result schema for the query, typed directly from the
|
|
256
|
+
* dry-run job's `statistics.query.schema` — never inferred client-side.
|
|
257
|
+
* `undefined` only when BigQuery's dry-run statistics omitted it. */
|
|
258
|
+
schema?: BigQueryResultSchemaField[];
|
|
214
259
|
}
|
|
215
260
|
export interface ExtractTableToGCSInput {
|
|
216
261
|
datasetId: string;
|
package/dist/chat/index.d.ts
CHANGED
|
@@ -41,32 +41,63 @@
|
|
|
41
41
|
* async (request) => recordReply(request.body),
|
|
42
42
|
* );
|
|
43
43
|
*
|
|
44
|
-
* Usage — the MCP server
|
|
45
|
-
*
|
|
46
|
-
*
|
|
44
|
+
* Usage — the MCP server the dispatched agent's clean room actually runs (AK-34): a
|
|
45
|
+
* `.danxbot/config/mcp-servers/*.yml` launches the PUBLISHED `app-kit-chat-mcp` bin
|
|
46
|
+
* directly, no vendored copy and no local install in the app's own repo. This
|
|
47
|
+
* package publishes FIVE bins and none is named `app-kit`, so `npx` needs
|
|
48
|
+
* `--package=` to say which package to fetch, separately from which bin to run —
|
|
49
|
+
* `npx -y @flytedesk/app-kit@<v> app-kit-chat-mcp` fails ("could not determine
|
|
50
|
+
* executable to run"); this is the form that actually works (verified against a real
|
|
51
|
+
* `npm pack` tarball run via `npx --package=<tgz>` from a clean directory with no
|
|
52
|
+
* `node_modules` — see `mcp/entry.test.ts`):
|
|
47
53
|
*
|
|
48
|
-
*
|
|
49
|
-
*
|
|
50
|
-
*
|
|
51
|
-
*
|
|
52
|
-
*
|
|
54
|
+
* server:
|
|
55
|
+
* command: npx
|
|
56
|
+
* args:
|
|
57
|
+
* - "-y"
|
|
58
|
+
* - "--package=@flytedesk/app-kit@6.0.0"
|
|
59
|
+
* - "app-kit-chat-mcp"
|
|
60
|
+
* - "--tools"
|
|
61
|
+
* - "${DANX_REPO_ROOT}/packages/audience-chat-mcp/src/chatTools.mjs"
|
|
53
62
|
*
|
|
54
|
-
*
|
|
55
|
-
*
|
|
56
|
-
*
|
|
57
|
-
*
|
|
58
|
-
*
|
|
59
|
-
*
|
|
60
|
-
*
|
|
61
|
-
*
|
|
62
|
-
*
|
|
63
|
-
*
|
|
63
|
+
* `--tools <path>` MUST be absolute — danxbot's cwd for this process is its own
|
|
64
|
+
* clean room, never assumed to equal the app's repo root, hence the
|
|
65
|
+
* `${DANX_REPO_ROOT}`-prefixed path above rather than a bare relative one.
|
|
66
|
+
*
|
|
67
|
+
* The path names the ONE thing this package cannot supply itself: the app's own
|
|
68
|
+
* tools module. That module must NOT `import` anything from `@flytedesk/app-kit` —
|
|
69
|
+
* a bare package import cannot resolve from danxbot's unbuilt dispatch clone (no
|
|
70
|
+
* `node_modules`). Instead, `app-kit-chat-mcp` INJECTS the pieces the module needs as
|
|
71
|
+
* a `ChatMcpKit` argument, and the module returns a (optionally wrapped)
|
|
72
|
+
* `ChatMcpProtocol` rather than a plain options bag:
|
|
73
|
+
*
|
|
74
|
+
* // chatTools.mjs — the app's own file, never vendored, never copied, never
|
|
75
|
+
* // imports @flytedesk/app-kit as a VALUE (a type-only `// @ts-check` JSDoc
|
|
76
|
+
* // reference to BuildChatServer/ChatMcpKit is fine — see mcp/entry.ts's header)
|
|
77
|
+
* export default function buildChatServer(kit) {
|
|
78
|
+
* return kit.createChatMcpProtocol({
|
|
79
|
+
* serverName: "sms-app-chat",
|
|
80
|
+
* tools: [
|
|
81
|
+
* kit.createReplyTool({ description: "...", deliver: postReplyToApi }),
|
|
82
|
+
* // The extension point: register whatever app-specific tools the agent
|
|
83
|
+
* // needs — each is just { name, description, inputSchema, handler }.
|
|
84
|
+
* { name: "audience_validate", description: "...", inputSchema: {...}, handler: validateAudience },
|
|
85
|
+
* ],
|
|
86
|
+
* });
|
|
87
|
+
* }
|
|
88
|
+
*
|
|
89
|
+
* `app-kit-chat-mcp` (`src/chat/mcp/entry.ts`, published as `dist/chat/mcp/entry.js`)
|
|
90
|
+
* loads that module, calls its default export with the kit, and starts the stdio
|
|
91
|
+
* loop on whatever `ChatMcpProtocol` it returns — see `entry.ts`'s own doc comment
|
|
92
|
+
* for the full contract, why it stays dependency-free, and how a consumer (e.g.
|
|
93
|
+
* media-planner's MP-169 `loggedProtocol`) wraps `handleMessage` before returning.
|
|
64
94
|
*/
|
|
65
|
-
export { createDanxbotLauncher, DanxbotError, isFailedJobStatus, isTerminalJobStatus } from "./launcher.js";
|
|
66
|
-
export { callbackSecretMatches, requireCallbackSecret } from "./callback-secret.js";
|
|
95
|
+
export { createDanxbotLauncher, DanxbotError, isFailedJobStatus, isTerminalJobStatus, } from "./launcher.js";
|
|
96
|
+
export { callbackSecretMatches, requireCallbackSecret, } from "./callback-secret.js";
|
|
67
97
|
export { createChatMcpProtocol, createReplyTool, parseReplyArguments, FALLBACK_PROTOCOL_VERSION, JSON_RPC, } from "./mcp-protocol.js";
|
|
68
98
|
export { createChatMcpStdioServer } from "./mcp-server.js";
|
|
69
99
|
export { parseChatEnv, ChatEnvError } from "./env.js";
|
|
70
100
|
export type { AppRegisteredTool, ChatEnv, ChatToolCallResult, ChatToolContent, DanxbotDispatchResult, DanxbotJobStatus, DanxbotLauncher, DanxbotLauncherOptions, } from "./types.js";
|
|
71
101
|
export type { ChatMcpProtocol, ChatMcpProtocolOptions, ChatReplyPayload, ParseReplyArgumentsOptions, ReplyDeliverResult, ReplyToolOptions, } from "./mcp-protocol.js";
|
|
72
|
-
export type { ChatMcpStdioServer, ChatMcpStdioServerOptions } from "./mcp-server.js";
|
|
102
|
+
export type { ChatMcpStdioServer, ChatMcpStdioServerOptions, } from "./mcp-server.js";
|
|
103
|
+
export type { BuildChatServer, ChatMcpKit } from "./mcp/entry.js";
|
package/dist/chat/index.js
CHANGED
|
@@ -41,29 +41,59 @@
|
|
|
41
41
|
* async (request) => recordReply(request.body),
|
|
42
42
|
* );
|
|
43
43
|
*
|
|
44
|
-
* Usage — the MCP server
|
|
45
|
-
*
|
|
46
|
-
*
|
|
44
|
+
* Usage — the MCP server the dispatched agent's clean room actually runs (AK-34): a
|
|
45
|
+
* `.danxbot/config/mcp-servers/*.yml` launches the PUBLISHED `app-kit-chat-mcp` bin
|
|
46
|
+
* directly, no vendored copy and no local install in the app's own repo. This
|
|
47
|
+
* package publishes FIVE bins and none is named `app-kit`, so `npx` needs
|
|
48
|
+
* `--package=` to say which package to fetch, separately from which bin to run —
|
|
49
|
+
* `npx -y @flytedesk/app-kit@<v> app-kit-chat-mcp` fails ("could not determine
|
|
50
|
+
* executable to run"); this is the form that actually works (verified against a real
|
|
51
|
+
* `npm pack` tarball run via `npx --package=<tgz>` from a clean directory with no
|
|
52
|
+
* `node_modules` — see `mcp/entry.test.ts`):
|
|
47
53
|
*
|
|
48
|
-
*
|
|
49
|
-
*
|
|
50
|
-
*
|
|
51
|
-
*
|
|
52
|
-
*
|
|
54
|
+
* server:
|
|
55
|
+
* command: npx
|
|
56
|
+
* args:
|
|
57
|
+
* - "-y"
|
|
58
|
+
* - "--package=@flytedesk/app-kit@6.0.0"
|
|
59
|
+
* - "app-kit-chat-mcp"
|
|
60
|
+
* - "--tools"
|
|
61
|
+
* - "${DANX_REPO_ROOT}/packages/audience-chat-mcp/src/chatTools.mjs"
|
|
53
62
|
*
|
|
54
|
-
*
|
|
55
|
-
*
|
|
56
|
-
*
|
|
57
|
-
*
|
|
58
|
-
*
|
|
59
|
-
*
|
|
60
|
-
*
|
|
61
|
-
*
|
|
62
|
-
*
|
|
63
|
-
*
|
|
63
|
+
* `--tools <path>` MUST be absolute — danxbot's cwd for this process is its own
|
|
64
|
+
* clean room, never assumed to equal the app's repo root, hence the
|
|
65
|
+
* `${DANX_REPO_ROOT}`-prefixed path above rather than a bare relative one.
|
|
66
|
+
*
|
|
67
|
+
* The path names the ONE thing this package cannot supply itself: the app's own
|
|
68
|
+
* tools module. That module must NOT `import` anything from `@flytedesk/app-kit` —
|
|
69
|
+
* a bare package import cannot resolve from danxbot's unbuilt dispatch clone (no
|
|
70
|
+
* `node_modules`). Instead, `app-kit-chat-mcp` INJECTS the pieces the module needs as
|
|
71
|
+
* a `ChatMcpKit` argument, and the module returns a (optionally wrapped)
|
|
72
|
+
* `ChatMcpProtocol` rather than a plain options bag:
|
|
73
|
+
*
|
|
74
|
+
* // chatTools.mjs — the app's own file, never vendored, never copied, never
|
|
75
|
+
* // imports @flytedesk/app-kit as a VALUE (a type-only `// @ts-check` JSDoc
|
|
76
|
+
* // reference to BuildChatServer/ChatMcpKit is fine — see mcp/entry.ts's header)
|
|
77
|
+
* export default function buildChatServer(kit) {
|
|
78
|
+
* return kit.createChatMcpProtocol({
|
|
79
|
+
* serverName: "sms-app-chat",
|
|
80
|
+
* tools: [
|
|
81
|
+
* kit.createReplyTool({ description: "...", deliver: postReplyToApi }),
|
|
82
|
+
* // The extension point: register whatever app-specific tools the agent
|
|
83
|
+
* // needs — each is just { name, description, inputSchema, handler }.
|
|
84
|
+
* { name: "audience_validate", description: "...", inputSchema: {...}, handler: validateAudience },
|
|
85
|
+
* ],
|
|
86
|
+
* });
|
|
87
|
+
* }
|
|
88
|
+
*
|
|
89
|
+
* `app-kit-chat-mcp` (`src/chat/mcp/entry.ts`, published as `dist/chat/mcp/entry.js`)
|
|
90
|
+
* loads that module, calls its default export with the kit, and starts the stdio
|
|
91
|
+
* loop on whatever `ChatMcpProtocol` it returns — see `entry.ts`'s own doc comment
|
|
92
|
+
* for the full contract, why it stays dependency-free, and how a consumer (e.g.
|
|
93
|
+
* media-planner's MP-169 `loggedProtocol`) wraps `handleMessage` before returning.
|
|
64
94
|
*/
|
|
65
|
-
export { createDanxbotLauncher, DanxbotError, isFailedJobStatus, isTerminalJobStatus } from "./launcher.js";
|
|
66
|
-
export { callbackSecretMatches, requireCallbackSecret } from "./callback-secret.js";
|
|
95
|
+
export { createDanxbotLauncher, DanxbotError, isFailedJobStatus, isTerminalJobStatus, } from "./launcher.js";
|
|
96
|
+
export { callbackSecretMatches, requireCallbackSecret, } from "./callback-secret.js";
|
|
67
97
|
export { createChatMcpProtocol, createReplyTool, parseReplyArguments, FALLBACK_PROTOCOL_VERSION, JSON_RPC, } from "./mcp-protocol.js";
|
|
68
98
|
export { createChatMcpStdioServer } from "./mcp-server.js";
|
|
69
99
|
export { parseChatEnv, ChatEnvError } from "./env.js";
|
package/dist/chat/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/chat/index.ts"],"names":[],"mappings":"AAAA
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/chat/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA6FG;AACH,OAAO,EACL,qBAAqB,EACrB,YAAY,EACZ,iBAAiB,EACjB,mBAAmB,GACpB,MAAM,eAAe,CAAC;AACvB,OAAO,EACL,qBAAqB,EACrB,qBAAqB,GACtB,MAAM,sBAAsB,CAAC;AAC9B,OAAO,EACL,qBAAqB,EACrB,eAAe,EACf,mBAAmB,EACnB,yBAAyB,EACzB,QAAQ,GACT,MAAM,mBAAmB,CAAC;AAC3B,OAAO,EAAE,wBAAwB,EAAE,MAAM,iBAAiB,CAAC;AAC3D,OAAO,EAAE,YAAY,EAAE,YAAY,EAAE,MAAM,UAAU,CAAC"}
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
import { createChatMcpProtocol, createReplyTool, parseReplyArguments, type ChatMcpProtocol } from "../mcp-protocol.js";
|
|
3
|
+
/**
|
|
4
|
+
* Everything a `--tools` module needs, injected rather than imported — see this
|
|
5
|
+
* file's own doc comment for why a runtime `import "@flytedesk/app-kit/chat"` from
|
|
6
|
+
* the module itself cannot work in danxbot's unbuilt dispatch clone.
|
|
7
|
+
*/
|
|
8
|
+
export interface ChatMcpKit {
|
|
9
|
+
createReplyTool: typeof createReplyTool;
|
|
10
|
+
parseReplyArguments: typeof parseReplyArguments;
|
|
11
|
+
createChatMcpProtocol: typeof createChatMcpProtocol;
|
|
12
|
+
}
|
|
13
|
+
/** The contract a `--tools` module's default export must satisfy: given the kit,
|
|
14
|
+
* return a (possibly wrapped) `ChatMcpProtocol`. */
|
|
15
|
+
export type BuildChatServer = (kit: ChatMcpKit) => ChatMcpProtocol | Promise<ChatMcpProtocol>;
|
|
16
|
+
export declare class ChatMcpEntryError extends Error {
|
|
17
|
+
}
|
|
18
|
+
export interface ChatMcpEntryArgs {
|
|
19
|
+
/** Path to the app's tools module. MUST be absolute in real dispatch use (see this
|
|
20
|
+
* file's own doc comment) — resolved against `process.cwd()` only as a fallback
|
|
21
|
+
* for a genuinely relative path, which `path.resolve` leaves untouched when the
|
|
22
|
+
* input is already absolute. */
|
|
23
|
+
toolsPath: string;
|
|
24
|
+
}
|
|
25
|
+
/** Parse argv into `{toolsPath}`. Exported for direct unit testing. */
|
|
26
|
+
export declare function parseArgs(argv: string[]): ChatMcpEntryArgs;
|
|
27
|
+
/**
|
|
28
|
+
* Dynamically `import()` the app's tools module and validate its default export.
|
|
29
|
+
* Exported for direct unit testing (no process spawn needed to exercise the loading
|
|
30
|
+
* and validation logic — see entry.test.ts's own split between a fast in-process
|
|
31
|
+
* suite for this function and real stdio/npx spawn suites for the whole process).
|
|
32
|
+
*/
|
|
33
|
+
export declare function loadBuildChatServer(toolsPath: string): Promise<BuildChatServer>;
|
|
34
|
+
/** Run the server: load the tools module, build the protocol (via the injected
|
|
35
|
+
* kit), and start stdio. Exported for direct unit testing of argument/validation
|
|
36
|
+
* failures without a process spawn. */
|
|
37
|
+
export declare function main(argv: string[]): Promise<void>;
|
|
@@ -0,0 +1,135 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
/**
|
|
3
|
+
* app-kit-chat-mcp (AK-34) — a runnable stdio entry point for
|
|
4
|
+
* `@flytedesk/app-kit/chat`'s MCP server, so a consuming app's
|
|
5
|
+
* `.danxbot/config/mcp-servers/*.yml` can launch a real chat MCP server without
|
|
6
|
+
* vendoring a copy of `mcp-protocol.js`/`mcp-server.js` into its own repo.
|
|
7
|
+
*
|
|
8
|
+
* ## Launch form
|
|
9
|
+
*
|
|
10
|
+
* This package publishes FIVE bins and none is named `app-kit`, so npx needs
|
|
11
|
+
* `--package=` to say which package to fetch (separate from which bin to run):
|
|
12
|
+
*
|
|
13
|
+
* npx -y --package=@flytedesk/app-kit@<v> app-kit-chat-mcp --tools <abs path>
|
|
14
|
+
*
|
|
15
|
+
* `npx -y @flytedesk/app-kit@<v> app-kit-chat-mcp` (no `--package=`) fails —
|
|
16
|
+
* verified against a real `npm pack` tarball run via `npx --package=<tgz>` from a
|
|
17
|
+
* clean directory with no `node_modules` (see entry.test.ts's "packed via npm pack,
|
|
18
|
+
* run via npx --package" suite).
|
|
19
|
+
*
|
|
20
|
+
* `--tools <path>` is resolved against `process.cwd()`, which danxbot sets to its
|
|
21
|
+
* OWN clean-room working directory — an app's yml MUST pass a
|
|
22
|
+
* `${DANX_REPO_ROOT}`-absolute path, never a bare relative one.
|
|
23
|
+
*
|
|
24
|
+
* ## The `--tools` module — kit injection, not a package import
|
|
25
|
+
*
|
|
26
|
+
* The module must NOT `import` `@flytedesk/app-kit` itself — that cannot resolve
|
|
27
|
+
* from danxbot's unbuilt dispatch clone (no `node_modules`), and would just move the
|
|
28
|
+
* vendoring problem this card closes into "the app re-implements createReplyTool".
|
|
29
|
+
* Instead this file injects a `ChatMcpKit` (`createReplyTool`, `parseReplyArguments`,
|
|
30
|
+
* `createChatMcpProtocol` — the same values `@flytedesk/app-kit/chat` exports) as
|
|
31
|
+
* the module's one argument:
|
|
32
|
+
*
|
|
33
|
+
* export default function build(kit) {
|
|
34
|
+
* return kit.createChatMcpProtocol({
|
|
35
|
+
* serverName: "my-app-chat",
|
|
36
|
+
* tools: [kit.createReplyTool({ description: "...", deliver: postReply }), ...],
|
|
37
|
+
* });
|
|
38
|
+
* }
|
|
39
|
+
*
|
|
40
|
+
* The default export returns a `ChatMcpProtocol` (`{handleMessage}`), not a plain
|
|
41
|
+
* options bag, so it can wrap the built protocol before returning (e.g.
|
|
42
|
+
* media-planner's MP-169 `loggedProtocol`). A module wanting type-checking can
|
|
43
|
+
* reference the exported `BuildChatServer`/`ChatMcpKit` TYPES via a `// @ts-check`
|
|
44
|
+
* JSDoc `@type` comment — type-only, erased, resolves fine from the app's own dev
|
|
45
|
+
* environment; it must never become a value import.
|
|
46
|
+
*
|
|
47
|
+
* ## Dependency-free at runtime
|
|
48
|
+
*
|
|
49
|
+
* Node builtins + this package's own `mcp-protocol.js`/`mcp-server.js` (themselves
|
|
50
|
+
* dependency-free) + `../../cli/is-running-as-main.js` (also Node-builtins-only —
|
|
51
|
+
* deliberately not imported from `../../cli/sync-engine.js`, which pulls in
|
|
52
|
+
* `cross-spawn` for its own Prisma-shelling subcommands).
|
|
53
|
+
*/
|
|
54
|
+
import { pathToFileURL } from "node:url";
|
|
55
|
+
import { resolve as resolvePath } from "node:path";
|
|
56
|
+
import { createChatMcpProtocol, createReplyTool, parseReplyArguments, } from "../mcp-protocol.js";
|
|
57
|
+
import { createChatMcpStdioServer } from "../mcp-server.js";
|
|
58
|
+
import { isRunningAsMain } from "../../cli/is-running-as-main.js";
|
|
59
|
+
const KIT = {
|
|
60
|
+
createReplyTool,
|
|
61
|
+
parseReplyArguments,
|
|
62
|
+
createChatMcpProtocol,
|
|
63
|
+
};
|
|
64
|
+
export class ChatMcpEntryError extends Error {
|
|
65
|
+
}
|
|
66
|
+
/** Parse argv into `{toolsPath}`. Exported for direct unit testing. */
|
|
67
|
+
export function parseArgs(argv) {
|
|
68
|
+
let toolsPath;
|
|
69
|
+
for (let i = 0; i < argv.length; i += 1) {
|
|
70
|
+
const arg = argv[i];
|
|
71
|
+
if (arg === "--tools") {
|
|
72
|
+
toolsPath = argv[i + 1];
|
|
73
|
+
i += 1;
|
|
74
|
+
}
|
|
75
|
+
else {
|
|
76
|
+
throw new ChatMcpEntryError(`app-kit-chat-mcp: unrecognized argument "${arg}"`);
|
|
77
|
+
}
|
|
78
|
+
}
|
|
79
|
+
if (toolsPath === undefined || toolsPath === "") {
|
|
80
|
+
throw new ChatMcpEntryError("app-kit-chat-mcp: missing required --tools <absolute path to the app's tools module>");
|
|
81
|
+
}
|
|
82
|
+
return { toolsPath };
|
|
83
|
+
}
|
|
84
|
+
/**
|
|
85
|
+
* Dynamically `import()` the app's tools module and validate its default export.
|
|
86
|
+
* Exported for direct unit testing (no process spawn needed to exercise the loading
|
|
87
|
+
* and validation logic — see entry.test.ts's own split between a fast in-process
|
|
88
|
+
* suite for this function and real stdio/npx spawn suites for the whole process).
|
|
89
|
+
*/
|
|
90
|
+
export async function loadBuildChatServer(toolsPath) {
|
|
91
|
+
const absolutePath = resolvePath(process.cwd(), toolsPath);
|
|
92
|
+
let mod;
|
|
93
|
+
try {
|
|
94
|
+
mod = await import(pathToFileURL(absolutePath).href);
|
|
95
|
+
}
|
|
96
|
+
catch (cause) {
|
|
97
|
+
throw new ChatMcpEntryError(`app-kit-chat-mcp: could not load tools module "${toolsPath}" (resolved to ` +
|
|
98
|
+
`${absolutePath}): ${cause instanceof Error ? cause.message : String(cause)}`);
|
|
99
|
+
}
|
|
100
|
+
const candidate = mod.default;
|
|
101
|
+
if (typeof candidate !== "function") {
|
|
102
|
+
throw new ChatMcpEntryError(`app-kit-chat-mcp: tools module "${toolsPath}" must have a default export that is a ` +
|
|
103
|
+
`function returning a ChatMcpProtocol; got ${typeof candidate}.`);
|
|
104
|
+
}
|
|
105
|
+
return candidate;
|
|
106
|
+
}
|
|
107
|
+
/** Run the server: load the tools module, build the protocol (via the injected
|
|
108
|
+
* kit), and start stdio. Exported for direct unit testing of argument/validation
|
|
109
|
+
* failures without a process spawn. */
|
|
110
|
+
export async function main(argv) {
|
|
111
|
+
const { toolsPath } = parseArgs(argv);
|
|
112
|
+
const buildChatServer = await loadBuildChatServer(toolsPath);
|
|
113
|
+
let protocol;
|
|
114
|
+
try {
|
|
115
|
+
protocol = await buildChatServer(KIT);
|
|
116
|
+
}
|
|
117
|
+
catch (cause) {
|
|
118
|
+
throw new ChatMcpEntryError(`app-kit-chat-mcp: tools module "${toolsPath}" threw while building its chat server: ` +
|
|
119
|
+
`${cause instanceof Error ? cause.message : String(cause)}`);
|
|
120
|
+
}
|
|
121
|
+
if (protocol === null ||
|
|
122
|
+
typeof protocol !== "object" ||
|
|
123
|
+
typeof protocol.handleMessage !== "function") {
|
|
124
|
+
throw new ChatMcpEntryError(`app-kit-chat-mcp: tools module "${toolsPath}"'s default export must return a ` +
|
|
125
|
+
`ChatMcpProtocol ({handleMessage(message)}); got ${typeof protocol}.`);
|
|
126
|
+
}
|
|
127
|
+
createChatMcpStdioServer({ protocol }).start();
|
|
128
|
+
}
|
|
129
|
+
if (isRunningAsMain(import.meta.url)) {
|
|
130
|
+
main(process.argv.slice(2)).catch((err) => {
|
|
131
|
+
console.error(err instanceof ChatMcpEntryError ? err.message : err);
|
|
132
|
+
process.exitCode = 1;
|
|
133
|
+
});
|
|
134
|
+
}
|
|
135
|
+
//# sourceMappingURL=entry.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"entry.js","sourceRoot":"","sources":["../../../src/chat/mcp/entry.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export declare function isRunningAsMain(moduleUrl: string): boolean;
|
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Whether the module at `moduleUrl` (pass `import.meta.url`) is the one Node was
|
|
3
|
+
* invoked to run directly — the ESM successor to `require.main === module`, shared
|
|
4
|
+
* by every bin wrapper (trace-sync.ts/profile-sync.ts/flags-sync.ts/spa-server/cli.ts
|
|
5
|
+
* and src/chat/mcp/entry.ts) so there's one fix point instead of N copies of the
|
|
6
|
+
* same check. Node builtins ONLY (`node:fs`, `node:url`) — importing this file never
|
|
7
|
+
* drags in anything else, which is why src/chat/mcp/entry.ts imports it directly
|
|
8
|
+
* instead of importing `isRunningAsMain` from ./sync-engine.js: that file also pulls
|
|
9
|
+
* in `cross-spawn` (for its Prisma-shelling subcommands) at module scope, which would
|
|
10
|
+
* break entry.ts's own "Node builtins + this package's own chat modules only"
|
|
11
|
+
* dependency-free bar. sync-engine.ts re-exports this same function so its existing
|
|
12
|
+
* importers (trace-sync.ts et al.) are unaffected.
|
|
13
|
+
*
|
|
14
|
+
* A raw `import.meta.url === pathToFileURL(process.argv[1]).href` string comparison
|
|
15
|
+
* (the previous check) is NOT reliable: a package manager's bin shim can leave
|
|
16
|
+
* `argv[1]` pointing at a *symlink* while Node resolves the ESM module id through
|
|
17
|
+
* that symlink to the real target path for `import.meta.url` — so the two name the
|
|
18
|
+
* same file but compare unequal. This is not hypothetical: pnpm's
|
|
19
|
+
* `node_modules/<pkg>` is exactly such a symlink into its content-addressable
|
|
20
|
+
* store, and it reproduces on Windows for every real invocation path (`pnpm exec
|
|
21
|
+
* flags-sync init`, an npm-script `"flags-sync init"` run via `pnpm run`, etc.) —
|
|
22
|
+
* confirmed by instrumenting a real 0.3.0 install: `argv[1]` resolved to
|
|
23
|
+
* `...\node_modules\@flytedesk\app-kit\dist\cli\flags-sync.js` (the symlinked
|
|
24
|
+
* path) while `import.meta.url` resolved to
|
|
25
|
+
* `...\node_modules\.pnpm\@flytedesk+app-kit@0.3.0_.../node_modules\@flytedesk\app-kit\dist\cli\flags-sync.js`
|
|
26
|
+
* (the real store path). The mismatch meant `main()` was never called at all —
|
|
27
|
+
* every CLI entrypoint silently no-op'd: exit code 0, no output, nothing written.
|
|
28
|
+
*
|
|
29
|
+
* `realpathSync.native` (the real OS `realpath` syscall, not Node's pure-JS
|
|
30
|
+
* fallback — verified directly: the JS fallback leaves an on-disk casing mismatch
|
|
31
|
+
* unresolved, e.g. `RealFile.js` vs `realfile.js` compare unequal even though NTFS
|
|
32
|
+
* treats them as the same file, while `.native` correctly normalizes both to the
|
|
33
|
+
* same canonical path) resolves both sides to the same canonical filesystem path
|
|
34
|
+
* first — following symlinks AND normalizing casing — so the comparison is correct
|
|
35
|
+
* regardless of symlinks, path-separator style, or casing differences between how
|
|
36
|
+
* a shell/bin-shim sets `argv[1]` and how Node constructs `import.meta.url` for
|
|
37
|
+
* the same file.
|
|
38
|
+
*/
|
|
39
|
+
import { realpathSync } from "node:fs";
|
|
40
|
+
import { fileURLToPath } from "node:url";
|
|
41
|
+
export function isRunningAsMain(moduleUrl) {
|
|
42
|
+
if (process.argv[1] === undefined)
|
|
43
|
+
return false;
|
|
44
|
+
try {
|
|
45
|
+
return (realpathSync.native(fileURLToPath(moduleUrl)) ===
|
|
46
|
+
realpathSync.native(process.argv[1]));
|
|
47
|
+
}
|
|
48
|
+
catch {
|
|
49
|
+
// Either path doesn't exist / isn't resolvable (e.g. a REPL or a bundler that
|
|
50
|
+
// gives import.meta.url a non-file URL) — definitely not "run directly" then.
|
|
51
|
+
return false;
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
//# sourceMappingURL=is-running-as-main.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"is-running-as-main.js","sourceRoot":"","sources":["../../src/cli/is-running-as-main.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAqCG;AACH,OAAO,EAAE,YAAY,EAAE,MAAM,SAAS,CAAC;AACvC,OAAO,EAAE,aAAa,EAAE,MAAM,UAAU,CAAC;AAEzC,MAAM,UAAU,eAAe,CAAC,SAAiB;IAC/C,IAAI,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,KAAK,SAAS;QAAE,OAAO,KAAK,CAAC;IAChD,IAAI,CAAC;QACH,OAAO,CACL,YAAY,CAAC,MAAM,CAAC,aAAa,CAAC,SAAS,CAAC,CAAC;YAC7C,YAAY,CAAC,MAAM,CAAC,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC,CACrC,CAAC;IACJ,CAAC;IAAC,MAAM,CAAC;QACP,8EAA8E;QAC9E,8EAA8E;QAC9E,OAAO,KAAK,CAAC;IACf,CAAC;AACH,CAAC"}
|
|
@@ -1,35 +1,4 @@
|
|
|
1
|
-
|
|
2
|
-
* Whether the module at `moduleUrl` (pass `import.meta.url`) is the one Node was
|
|
3
|
-
* invoked to run directly — the ESM successor to `require.main === module`, shared
|
|
4
|
-
* by every bin wrapper (trace-sync.ts/profile-sync.ts/flags-sync.ts) so there's one
|
|
5
|
-
* fix point instead of three copies of the same check.
|
|
6
|
-
*
|
|
7
|
-
* A raw `import.meta.url === pathToFileURL(process.argv[1]).href` string comparison
|
|
8
|
-
* (the previous check) is NOT reliable: a package manager's bin shim can leave
|
|
9
|
-
* `argv[1]` pointing at a *symlink* while Node resolves the ESM module id through
|
|
10
|
-
* that symlink to the real target path for `import.meta.url` — so the two name the
|
|
11
|
-
* same file but compare unequal. This is not hypothetical: pnpm's
|
|
12
|
-
* `node_modules/<pkg>` is exactly such a symlink into its content-addressable
|
|
13
|
-
* store, and it reproduces on Windows for every real invocation path (`pnpm exec
|
|
14
|
-
* flags-sync init`, an npm-script `"flags-sync init"` run via `pnpm run`, etc.) —
|
|
15
|
-
* confirmed by instrumenting a real 0.3.0 install: `argv[1]` resolved to
|
|
16
|
-
* `...\node_modules\@flytedesk\app-kit\dist\cli\flags-sync.js` (the symlinked
|
|
17
|
-
* path) while `import.meta.url` resolved to
|
|
18
|
-
* `...\node_modules\.pnpm\@flytedesk+app-kit@0.3.0_.../node_modules\@flytedesk\app-kit\dist\cli\flags-sync.js`
|
|
19
|
-
* (the real store path). The mismatch meant `main()` was never called at all —
|
|
20
|
-
* every CLI entrypoint silently no-op'd: exit code 0, no output, nothing written.
|
|
21
|
-
*
|
|
22
|
-
* `realpathSync.native` (the real OS `realpath` syscall, not Node's pure-JS
|
|
23
|
-
* fallback — verified directly: the JS fallback leaves an on-disk casing mismatch
|
|
24
|
-
* unresolved, e.g. `RealFile.js` vs `realfile.js` compare unequal even though NTFS
|
|
25
|
-
* treats them as the same file, while `.native` correctly normalizes both to the
|
|
26
|
-
* same canonical path) resolves both sides to the same canonical filesystem path
|
|
27
|
-
* first — following symlinks AND normalizing casing — so the comparison is correct
|
|
28
|
-
* regardless of symlinks, path-separator style, or casing differences between how
|
|
29
|
-
* a shell/bin-shim sets `argv[1]` and how Node constructs `import.meta.url` for
|
|
30
|
-
* the same file.
|
|
31
|
-
*/
|
|
32
|
-
export declare function isRunningAsMain(moduleUrl: string): boolean;
|
|
1
|
+
export { isRunningAsMain } from "./is-running-as-main.js";
|
|
33
2
|
/**
|
|
34
3
|
* Everything that differs between `trace-sync` and `profile-sync`. `name` picks out
|
|
35
4
|
* prisma/fragments/<name>.prisma, prisma/fragments/<name>.meta.json, and every
|
package/dist/cli/sync-engine.js
CHANGED
|
@@ -53,53 +53,15 @@
|
|
|
53
53
|
*/
|
|
54
54
|
import { createHash } from "node:crypto";
|
|
55
55
|
import spawn from "cross-spawn";
|
|
56
|
-
import { copyFileSync, cpSync, existsSync, mkdirSync, readFileSync,
|
|
56
|
+
import { copyFileSync, cpSync, existsSync, mkdirSync, readFileSync, writeFileSync, } from "node:fs";
|
|
57
57
|
import path from "node:path";
|
|
58
58
|
import { fileURLToPath, pathToFileURL } from "node:url";
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
* A raw `import.meta.url === pathToFileURL(process.argv[1]).href` string comparison
|
|
66
|
-
* (the previous check) is NOT reliable: a package manager's bin shim can leave
|
|
67
|
-
* `argv[1]` pointing at a *symlink* while Node resolves the ESM module id through
|
|
68
|
-
* that symlink to the real target path for `import.meta.url` — so the two name the
|
|
69
|
-
* same file but compare unequal. This is not hypothetical: pnpm's
|
|
70
|
-
* `node_modules/<pkg>` is exactly such a symlink into its content-addressable
|
|
71
|
-
* store, and it reproduces on Windows for every real invocation path (`pnpm exec
|
|
72
|
-
* flags-sync init`, an npm-script `"flags-sync init"` run via `pnpm run`, etc.) —
|
|
73
|
-
* confirmed by instrumenting a real 0.3.0 install: `argv[1]` resolved to
|
|
74
|
-
* `...\node_modules\@flytedesk\app-kit\dist\cli\flags-sync.js` (the symlinked
|
|
75
|
-
* path) while `import.meta.url` resolved to
|
|
76
|
-
* `...\node_modules\.pnpm\@flytedesk+app-kit@0.3.0_.../node_modules\@flytedesk\app-kit\dist\cli\flags-sync.js`
|
|
77
|
-
* (the real store path). The mismatch meant `main()` was never called at all —
|
|
78
|
-
* every CLI entrypoint silently no-op'd: exit code 0, no output, nothing written.
|
|
79
|
-
*
|
|
80
|
-
* `realpathSync.native` (the real OS `realpath` syscall, not Node's pure-JS
|
|
81
|
-
* fallback — verified directly: the JS fallback leaves an on-disk casing mismatch
|
|
82
|
-
* unresolved, e.g. `RealFile.js` vs `realfile.js` compare unequal even though NTFS
|
|
83
|
-
* treats them as the same file, while `.native` correctly normalizes both to the
|
|
84
|
-
* same canonical path) resolves both sides to the same canonical filesystem path
|
|
85
|
-
* first — following symlinks AND normalizing casing — so the comparison is correct
|
|
86
|
-
* regardless of symlinks, path-separator style, or casing differences between how
|
|
87
|
-
* a shell/bin-shim sets `argv[1]` and how Node constructs `import.meta.url` for
|
|
88
|
-
* the same file.
|
|
89
|
-
*/
|
|
90
|
-
export function isRunningAsMain(moduleUrl) {
|
|
91
|
-
if (process.argv[1] === undefined)
|
|
92
|
-
return false;
|
|
93
|
-
try {
|
|
94
|
-
return (realpathSync.native(fileURLToPath(moduleUrl)) ===
|
|
95
|
-
realpathSync.native(process.argv[1]));
|
|
96
|
-
}
|
|
97
|
-
catch {
|
|
98
|
-
// Either path doesn't exist / isn't resolvable (e.g. a REPL or a bundler that
|
|
99
|
-
// gives import.meta.url a non-file URL) — definitely not "run directly" then.
|
|
100
|
-
return false;
|
|
101
|
-
}
|
|
102
|
-
}
|
|
59
|
+
// Re-exported so trace-sync.ts/profile-sync.ts/flags-sync.ts keep importing it from
|
|
60
|
+
// here unchanged — the implementation (and its symlink/casing bug history) now lives
|
|
61
|
+
// in its own Node-builtins-only module because src/chat/mcp/entry.ts needs the exact
|
|
62
|
+
// same check WITHOUT this file's `cross-spawn` import coming along for the ride. See
|
|
63
|
+
// is-running-as-main.ts's own doc comment for the full contract.
|
|
64
|
+
export { isRunningAsMain } from "./is-running-as-main.js";
|
|
103
65
|
export class SyncCliError extends Error {
|
|
104
66
|
}
|
|
105
67
|
function readJson(filePath) {
|