@noodleseed/one 0.158.0 → 0.160.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/commands/deploy-first-flow.d.ts +1 -1
- package/dist/commands/deploy-first-flow.d.ts.map +1 -1
- package/dist/commands/deploy-first-flow.js +4 -193
- package/dist/commands/deploy-first-flow.js.map +1 -1
- package/dist/commands/deploy-ops.d.ts.map +1 -1
- package/dist/commands/deploy-ops.js +25 -1
- package/dist/commands/deploy-ops.js.map +1 -1
- package/dist/commands/deploy-preflight-command.d.ts +8 -0
- package/dist/commands/deploy-preflight-command.d.ts.map +1 -0
- package/dist/commands/deploy-preflight-command.js +78 -0
- package/dist/commands/deploy-preflight-command.js.map +1 -0
- package/dist/commands/deploy-preflight-output.d.ts +18 -0
- package/dist/commands/deploy-preflight-output.d.ts.map +1 -0
- package/dist/commands/deploy-preflight-output.js +176 -0
- package/dist/commands/deploy-preflight-output.js.map +1 -0
- package/dist/commands/deploy-preflight.d.ts +6 -1
- package/dist/commands/deploy-preflight.d.ts.map +1 -1
- package/dist/commands/deploy-preflight.js +24 -0
- package/dist/commands/deploy-preflight.js.map +1 -1
- package/dist/commands/deploy-target.d.ts +1 -0
- package/dist/commands/deploy-target.d.ts.map +1 -1
- package/dist/commands/deploy-target.js +9 -8
- package/dist/commands/deploy-target.js.map +1 -1
- package/dist/openapi-import.d.ts.map +1 -1
- package/dist/openapi-import.js +51 -23
- package/dist/openapi-import.js.map +1 -1
- package/dist/plugin-mode/build-readiness-recorder.d.ts.map +1 -1
- package/dist/plugin-mode/build-readiness-recorder.js +3 -0
- package/dist/plugin-mode/build-readiness-recorder.js.map +1 -1
- package/dist/plugin-mode/plugin-operation-contract.d.ts +120 -1
- package/dist/plugin-mode/plugin-operation-contract.d.ts.map +1 -1
- package/dist/plugin-mode/plugin-operation-contract.js +32 -1
- package/dist/plugin-mode/plugin-operation-contract.js.map +1 -1
- package/dist/plugin-mode/plugin-operation-tools.d.ts.map +1 -1
- package/dist/plugin-mode/plugin-operation-tools.js +47 -2
- package/dist/plugin-mode/plugin-operation-tools.js.map +1 -1
- package/node_modules/@noodle-borg/agent-kit/dist/generated/example-files.js +1 -1
- package/node_modules/@noodle-borg/agent-kit/dist/plugin-bootstrap-skill.js +1 -1
- package/node_modules/@noodle-borg/agent-kit/dist/skill-authoring-refs.js +1 -1
- package/node_modules/@noodle-borg/agent-kit/dist/skill-operations-refs.js +2 -1
- package/node_modules/@noodle-borg/agent-kit/dist/skill-router.js +1 -1
- package/node_modules/@noodle-borg/agent-kit/dist/skill-verification-ref.js +1 -0
- package/node_modules/@noodle-borg/agent-kit/package.json +1 -1
- package/node_modules/@noodle-borg/cli-catalog/dist/catalog-data-hosted-deployment.js +85 -68
- package/node_modules/@noodle-borg/control-plane/dist/contracts.d.ts +5 -0
- package/node_modules/@noodle-borg/control-plane/dist/contracts.js +9 -0
- package/node_modules/@noodle-borg/control-plane/dist/in-memory-control-plane-store.d.ts +16 -1
- package/node_modules/@noodle-borg/control-plane/dist/in-memory-control-plane-store.js +18 -0
- package/node_modules/@noodle-borg/control-plane/dist/portable.d.ts +1 -0
- package/node_modules/@noodle-borg/control-plane/dist/portable.js +1 -0
- package/node_modules/@noodle-borg/openapi-import/dist/index.js +76 -53
- package/node_modules/@noodle-borg/openapi-import/dist/request-body.js +125 -0
- package/node_modules/@noodle-borg/openapi-import/dist/request-input.js +139 -0
- package/node_modules/@noodle-borg/openapi-import/dist/response-schema.js +17 -4
- package/node_modules/@noodle-borg/service/dist/routes/org-admin.js +21 -11
- package/node_modules/@noodle-borg/service/package.json +1 -1
- package/package.json +2 -2
|
@@ -0,0 +1,139 @@
|
|
|
1
|
+
const FIELD = /^[A-Za-z_][A-Za-z0-9_]*$/;
|
|
2
|
+
const RESERVED_FIELDS = new Set([
|
|
3
|
+
'then',
|
|
4
|
+
'equals',
|
|
5
|
+
'at',
|
|
6
|
+
'optional',
|
|
7
|
+
'toExpression',
|
|
8
|
+
'toString',
|
|
9
|
+
'__proto__',
|
|
10
|
+
'constructor',
|
|
11
|
+
'prototype',
|
|
12
|
+
]);
|
|
13
|
+
export function identifier(value) {
|
|
14
|
+
const out = value.replace(/[^A-Za-z0-9_]+/g, '_').replace(/^_+|_+$/g, '');
|
|
15
|
+
return /^[A-Za-z_]/.test(out) ? out : `op_${out || 'operation'}`;
|
|
16
|
+
}
|
|
17
|
+
/** Keep wire query names intact; path placeholder names may be normalized with their arguments. */
|
|
18
|
+
export function importOperationParameters(path, inherited, own) {
|
|
19
|
+
const selected = new Map();
|
|
20
|
+
for (const list of [inherited, own]) {
|
|
21
|
+
if (list === undefined)
|
|
22
|
+
continue;
|
|
23
|
+
if (!Array.isArray(list))
|
|
24
|
+
throw new Error('import openapi: parameters must be an array');
|
|
25
|
+
for (const value of list) {
|
|
26
|
+
if (!isRecord(value) ||
|
|
27
|
+
typeof value.name !== 'string' ||
|
|
28
|
+
value.name.length === 0 ||
|
|
29
|
+
value.name.length > 128 ||
|
|
30
|
+
/\p{Cc}/u.test(value.name)) {
|
|
31
|
+
throw new Error('import openapi: use inline parameters with bounded, non-control names');
|
|
32
|
+
}
|
|
33
|
+
if (value.in !== 'path' && value.in !== 'query') {
|
|
34
|
+
throw new Error('import openapi: only path/query parameters are supported; map other locations explicitly in TypeScript');
|
|
35
|
+
}
|
|
36
|
+
if (value.style !== undefined && value.style !== (value.in === 'path' ? 'simple' : 'form')) {
|
|
37
|
+
throw new Error('import openapi: unsupported parameter serialization style; map it explicitly in TypeScript');
|
|
38
|
+
}
|
|
39
|
+
if (value.allowReserved === true ||
|
|
40
|
+
value.allowEmptyValue === true ||
|
|
41
|
+
value.content !== undefined) {
|
|
42
|
+
throw new Error('import openapi: unsupported parameter wire encoding; map it explicitly in TypeScript');
|
|
43
|
+
}
|
|
44
|
+
selected.set(`${value.in}:${value.name}`, value);
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
if (selected.size > 200)
|
|
48
|
+
throw new Error('import openapi: operation exceeds 200 parameters');
|
|
49
|
+
const names = new Set();
|
|
50
|
+
const pathNames = new Map();
|
|
51
|
+
const parameters = [...selected.values()].map((value) => {
|
|
52
|
+
const rawName = String(value.name);
|
|
53
|
+
let name = value.in === 'path' ? identifier(rawName) : rawName;
|
|
54
|
+
if (value.in === 'path' && RESERVED_FIELDS.has(name))
|
|
55
|
+
name = `param_${name}`;
|
|
56
|
+
if (!FIELD.test(name) || RESERVED_FIELDS.has(name)) {
|
|
57
|
+
throw new Error('import openapi: query parameter name cannot be addressed by the authoring expression contract; map this API through an application handler');
|
|
58
|
+
}
|
|
59
|
+
if (names.has(name))
|
|
60
|
+
throw new Error('import openapi: parameter input-name collision; map the inputs explicitly');
|
|
61
|
+
names.add(name);
|
|
62
|
+
if (value.in === 'path')
|
|
63
|
+
pathNames.set(rawName, name);
|
|
64
|
+
return {
|
|
65
|
+
name,
|
|
66
|
+
in: value.in === 'path' ? 'path' : 'query',
|
|
67
|
+
required: value.in === 'path' || value.required === true,
|
|
68
|
+
schema: parameterJsonSchema(value.schema),
|
|
69
|
+
};
|
|
70
|
+
});
|
|
71
|
+
const placeholders = [...path.matchAll(/\{([^{}]+)\}/g)].map((match) => match[1]);
|
|
72
|
+
if (placeholders.some((name) => name === undefined || !pathNames.has(name)) ||
|
|
73
|
+
[...pathNames.keys()].some((name) => !placeholders.includes(name))) {
|
|
74
|
+
throw new Error('import openapi: every path placeholder must match a declared path parameter');
|
|
75
|
+
}
|
|
76
|
+
return {
|
|
77
|
+
parameters,
|
|
78
|
+
connectorPath: path.replace(/\{([^{}]+)\}/g, (_match, name) => `{${pathNames.get(name)}}`),
|
|
79
|
+
};
|
|
80
|
+
}
|
|
81
|
+
function parameterJsonSchema(schema) {
|
|
82
|
+
if (schema === undefined)
|
|
83
|
+
return { type: 'string' };
|
|
84
|
+
if (!isRecord(schema))
|
|
85
|
+
throw new Error('import openapi: parameter schema must be an object');
|
|
86
|
+
if (schema.$ref !== undefined)
|
|
87
|
+
throw new Error('import openapi: inline parameter schemas before import; references are not fetched');
|
|
88
|
+
const allowed = new Set([
|
|
89
|
+
'type',
|
|
90
|
+
'nullable',
|
|
91
|
+
'enum',
|
|
92
|
+
'format',
|
|
93
|
+
'title',
|
|
94
|
+
'description',
|
|
95
|
+
'example',
|
|
96
|
+
'examples',
|
|
97
|
+
'deprecated',
|
|
98
|
+
]);
|
|
99
|
+
if (Object.keys(schema).some((key) => !allowed.has(key))) {
|
|
100
|
+
throw new Error('import openapi: unsupported parameter schema constraint; author its validated mapping explicitly');
|
|
101
|
+
}
|
|
102
|
+
const types = Array.isArray(schema.type)
|
|
103
|
+
? schema.type
|
|
104
|
+
: [schema.type ?? 'string'];
|
|
105
|
+
const concrete = types.filter((type) => type !== 'null');
|
|
106
|
+
const base = concrete[0];
|
|
107
|
+
if (concrete.length !== 1 ||
|
|
108
|
+
types.length > 2 ||
|
|
109
|
+
typeof base !== 'string' ||
|
|
110
|
+
!['string', 'number', 'integer', 'boolean'].includes(base)) {
|
|
111
|
+
throw new Error('import openapi: unsupported parameter schema; use a scalar or an explicitly authored mapping');
|
|
112
|
+
}
|
|
113
|
+
const enumValues = base === 'string' ? stringEnum(schema.enum) : undefined;
|
|
114
|
+
if (schema.enum !== undefined &&
|
|
115
|
+
(enumValues === undefined || types.includes('null') || schema.nullable === true)) {
|
|
116
|
+
throw new Error('import openapi: unsupported parameter enum/nullable combination; author an explicit mapping');
|
|
117
|
+
}
|
|
118
|
+
const format = typeof schema.format === 'string' ? schema.format : undefined;
|
|
119
|
+
if ((schema.nullable !== undefined && typeof schema.nullable !== 'boolean') ||
|
|
120
|
+
(schema.format !== undefined && format === undefined)) {
|
|
121
|
+
throw new Error('import openapi: invalid parameter nullable or format declaration');
|
|
122
|
+
}
|
|
123
|
+
return {
|
|
124
|
+
type: schema.nullable === true || types.includes('null') ? [base, 'null'] : base,
|
|
125
|
+
...(enumValues !== undefined ? { enum: enumValues } : {}),
|
|
126
|
+
...(format !== undefined ? { format } : {}),
|
|
127
|
+
};
|
|
128
|
+
}
|
|
129
|
+
function stringEnum(value) {
|
|
130
|
+
if (!Array.isArray(value) || value.length === 0)
|
|
131
|
+
return undefined;
|
|
132
|
+
if (!value.every((entry) => typeof entry === 'string'))
|
|
133
|
+
return undefined;
|
|
134
|
+
return value;
|
|
135
|
+
}
|
|
136
|
+
function isRecord(value) {
|
|
137
|
+
return value !== null && typeof value === 'object' && !Array.isArray(value);
|
|
138
|
+
}
|
|
139
|
+
//# sourceMappingURL=request-input.js.map
|
|
@@ -8,6 +8,10 @@ export const MAX_RESPONSE_SCHEMA_DEPTH = 8;
|
|
|
8
8
|
export const MAX_RESPONSE_SCHEMA_NODES = 200;
|
|
9
9
|
const UNKNOWN = { kind: 'unknown' };
|
|
10
10
|
const UNSUPPORTED_COMPOSITIONS = ['oneOf', 'anyOf', 'allOf', 'not'];
|
|
11
|
+
/** Shared bounded conversion; request validation is owned by request-body.ts. */
|
|
12
|
+
export function importSchemaTree(raw, ctx, request = false) {
|
|
13
|
+
return convertSchema(raw, ctx, { nodes: 0, boundWarned: false, request }, 0, new Set());
|
|
14
|
+
}
|
|
11
15
|
/**
|
|
12
16
|
* Picks the best 2xx `application/json` schema from an operation's `responses` block and
|
|
13
17
|
* converts it. Returns `undefined` (with warnings pushed to the context) when the operation
|
|
@@ -41,14 +45,17 @@ export function toOutputJsonSchema(schema) {
|
|
|
41
45
|
case 'string':
|
|
42
46
|
return withNullable({ type: 'string', ...(schema.enum !== undefined ? { enum: [...schema.enum] } : {}) }, schema.nullable);
|
|
43
47
|
case 'number':
|
|
44
|
-
return withNullable({ type: 'number' }, schema.nullable);
|
|
48
|
+
return withNullable({ type: schema.integer === true ? 'integer' : 'number' }, schema.nullable);
|
|
45
49
|
case 'boolean':
|
|
46
50
|
return withNullable({ type: 'boolean' }, schema.nullable);
|
|
47
51
|
case 'array':
|
|
48
52
|
return withNullable({ type: 'array', items: toOutputJsonSchema(schema.items) }, schema.nullable);
|
|
49
53
|
case 'object': {
|
|
54
|
+
const extra = schema.additionalProperties === undefined
|
|
55
|
+
? {}
|
|
56
|
+
: { additionalProperties: schema.additionalProperties };
|
|
50
57
|
if (schema.properties.length === 0)
|
|
51
|
-
return withNullable({ type: 'object' }, schema.nullable);
|
|
58
|
+
return withNullable({ type: 'object', ...extra }, schema.nullable);
|
|
52
59
|
const required = schema.properties
|
|
53
60
|
.filter((property) => property.required)
|
|
54
61
|
.map((property) => property.name);
|
|
@@ -59,6 +66,7 @@ export function toOutputJsonSchema(schema) {
|
|
|
59
66
|
toOutputJsonSchema(property.schema),
|
|
60
67
|
])),
|
|
61
68
|
...(required.length > 0 ? { required } : {}),
|
|
69
|
+
...extra,
|
|
62
70
|
}, schema.nullable);
|
|
63
71
|
}
|
|
64
72
|
case 'unknown':
|
|
@@ -101,7 +109,11 @@ function convertSchema(raw, ctx, state, depth, visitedRefs) {
|
|
|
101
109
|
}
|
|
102
110
|
case 'number':
|
|
103
111
|
case 'integer':
|
|
104
|
-
return {
|
|
112
|
+
return {
|
|
113
|
+
kind: 'number',
|
|
114
|
+
...nullableSpread,
|
|
115
|
+
...(state.request && type === 'integer' ? { integer: true } : {}),
|
|
116
|
+
};
|
|
105
117
|
case 'boolean':
|
|
106
118
|
return { kind: 'boolean', ...nullableSpread };
|
|
107
119
|
case 'array':
|
|
@@ -120,6 +132,7 @@ function convertSchema(raw, ctx, state, depth, visitedRefs) {
|
|
|
120
132
|
return {
|
|
121
133
|
kind: 'object',
|
|
122
134
|
...nullableSpread,
|
|
135
|
+
...(state.request ? { additionalProperties: raw.additionalProperties !== false } : {}),
|
|
123
136
|
properties: Object.entries(properties).map(([name, propertySchema]) => ({
|
|
124
137
|
name,
|
|
125
138
|
required: required.has(name),
|
|
@@ -143,7 +156,7 @@ function convertRef(ref, ctx, state, depth, visitedRefs) {
|
|
|
143
156
|
ctx.warnings.push(`${ctx.operationName}: response schema $ref "${ref}" is not a component schema reference; that part is left untyped`);
|
|
144
157
|
return UNKNOWN;
|
|
145
158
|
}
|
|
146
|
-
const name = ref.slice(prefix.length);
|
|
159
|
+
const name = ref.slice(prefix.length).replace(/~1/g, '/').replace(/~0/g, '~');
|
|
147
160
|
if (visitedRefs.has(name)) {
|
|
148
161
|
ctx.warnings.push(`${ctx.operationName}: response schema $ref "${ref}" is circular; the repeated part is left untyped`);
|
|
149
162
|
return UNKNOWN;
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { mcpSubdomainEndpointOptions } from '@noodle-borg/control-plane/portable';
|
|
1
|
+
import { mcpSubdomainEndpointOptions, PersonalWorkspaceOwnerMutationError, } from '@noodle-borg/control-plane/portable';
|
|
2
2
|
import { normalizePublicBaseDomain, OPENAI_APPS_CHALLENGE_PATH } from '@noodle-borg/module';
|
|
3
3
|
import { readJsonBody, sendJson } from '@noodle-borg/transport-http';
|
|
4
4
|
import { OrgSummarySchema } from '@noodle-borg/wire-contracts';
|
|
@@ -142,7 +142,7 @@ export async function handleMembers(req, res, gate, controlPlane, maxBody, ref,
|
|
|
142
142
|
return sendJson(res, 201, { ok: true, member });
|
|
143
143
|
}
|
|
144
144
|
catch (error) {
|
|
145
|
-
return
|
|
145
|
+
return sendMemberMutationError(res, error);
|
|
146
146
|
}
|
|
147
147
|
}
|
|
148
148
|
if (req.method === 'PATCH' && ref.subject !== undefined) {
|
|
@@ -177,7 +177,7 @@ export async function handleMembers(req, res, gate, controlPlane, maxBody, ref,
|
|
|
177
177
|
return sendJson(res, 200, { ok: true, member });
|
|
178
178
|
}
|
|
179
179
|
catch (error) {
|
|
180
|
-
return
|
|
180
|
+
return sendMemberMutationError(res, error);
|
|
181
181
|
}
|
|
182
182
|
}
|
|
183
183
|
if (req.method === 'DELETE' && ref.subject !== undefined) {
|
|
@@ -186,18 +186,28 @@ export async function handleMembers(req, res, gate, controlPlane, maxBody, ref,
|
|
|
186
186
|
if (await demotesLastOwner(controlPlane, ref.org, ref.subject, undefined)) {
|
|
187
187
|
return sendJson(res, 409, { error: 'cannot remove the last owner' });
|
|
188
188
|
}
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
189
|
+
try {
|
|
190
|
+
const removed = await controlPlane.removeOrgMember({ org: ref.org, subject: ref.subject });
|
|
191
|
+
if (removed) {
|
|
192
|
+
await emitOrgAudit(audit, 'org.member.removed', ref.org, identity, 204, {
|
|
193
|
+
subject: ref.subject,
|
|
194
|
+
});
|
|
195
|
+
}
|
|
196
|
+
res.writeHead(204);
|
|
197
|
+
res.end();
|
|
198
|
+
return;
|
|
199
|
+
}
|
|
200
|
+
catch (error) {
|
|
201
|
+
return sendMemberMutationError(res, error);
|
|
194
202
|
}
|
|
195
|
-
res.writeHead(204);
|
|
196
|
-
res.end();
|
|
197
|
-
return;
|
|
198
203
|
}
|
|
199
204
|
return sendJson(res, 404, { error: 'not found' });
|
|
200
205
|
}
|
|
206
|
+
function sendMemberMutationError(res, error) {
|
|
207
|
+
sendJson(res, error instanceof PersonalWorkspaceOwnerMutationError ? 409 : 400, {
|
|
208
|
+
error: error.message,
|
|
209
|
+
});
|
|
210
|
+
}
|
|
201
211
|
export async function handleInvitations(req, res, gate, controlPlane, maxBody, ref, audit, url, invitationEmail = {}, now = () => new Date()) {
|
|
202
212
|
const identity = await authorizeControlPlane(req, res, gate, { requireIdentity: true });
|
|
203
213
|
if (identity === false)
|
|
@@ -39,7 +39,7 @@
|
|
|
39
39
|
"dependencies": {
|
|
40
40
|
"@modelcontextprotocol/sdk": "^1.29.0",
|
|
41
41
|
"@noodle-borg/admission-limits": "0.0.0",
|
|
42
|
-
"@noodle-borg/agent-kit": "0.
|
|
42
|
+
"@noodle-borg/agent-kit": "0.97.0",
|
|
43
43
|
"@noodle-borg/app-package": "0.0.0",
|
|
44
44
|
"@noodle-borg/assistant-gateway": "0.0.0",
|
|
45
45
|
"@noodle-borg/auth": "0.0.0",
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@noodleseed/one",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.160.0",
|
|
4
4
|
"private": false,
|
|
5
5
|
"description": "Noodle CLI by Noodle Seed — author, run, and deploy declarative MCP servers. Embedding the assistant in your own web app is @noodleseed/assistant.",
|
|
6
6
|
"license": "Apache-2.0",
|
|
@@ -235,7 +235,7 @@
|
|
|
235
235
|
"@modelcontextprotocol/client": "2.0.0",
|
|
236
236
|
"@modelcontextprotocol/server": "2.0.0",
|
|
237
237
|
"@noodle-borg/admission-limits": "0.0.0",
|
|
238
|
-
"@noodle-borg/agent-kit": "0.
|
|
238
|
+
"@noodle-borg/agent-kit": "0.97.0",
|
|
239
239
|
"@noodle-borg/app-audit": "0.0.0",
|
|
240
240
|
"@noodle-borg/app-package": "0.0.0",
|
|
241
241
|
"@noodle-borg/assistant-gateway": "0.0.0",
|