@moda-ai/cli 1.20.0 → 1.21.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/init/index.d.ts +2 -0
- package/dist/init/index.js +101 -26
- package/dist/init/sdk-catalog.d.ts +10 -0
- package/dist/init/sdk-catalog.js +80 -0
- package/dist/init/sdk-guide.d.ts +5 -34
- package/dist/init/sdk-guide.js +74 -290
- package/dist/init/sdk-integration.d.ts +43 -44
- package/dist/init/sdk-integration.js +796 -133
- package/dist/init/sdk-protocol.d.ts +135 -0
- package/dist/init/sdk-protocol.js +152 -0
- package/dist/init/sdk-verification.d.ts +27 -0
- package/dist/init/sdk-verification.js +123 -0
- package/dist/sync.js +11 -2
- package/package.json +1 -1
- package/skills/integration/index.json +2 -2
- package/dist/init/skill-resolver.d.ts +0 -19
- package/dist/init/skill-resolver.js +0 -78
|
@@ -0,0 +1,135 @@
|
|
|
1
|
+
import { z } from 'zod';
|
|
2
|
+
export declare const SDK_RESULT_SCHEMA_VERSION = "moda.sdk_integration_result.v1";
|
|
3
|
+
export declare const SDK_RESULT_JSON_BEGIN = "MODA_SDK_RESULT_JSON_BEGIN";
|
|
4
|
+
export declare const SDK_RESULT_JSON_END = "MODA_SDK_RESULT_JSON_END";
|
|
5
|
+
export declare const SDK_RESULT_FILE = ".moda/sdk-integration-result.json";
|
|
6
|
+
export declare const SdkAgentResultSchema: z.ZodObject<{
|
|
7
|
+
schema_version: z.ZodPipeline<z.ZodString, z.ZodLiteral<"moda.sdk_integration_result.v1">>;
|
|
8
|
+
status: z.ZodEffects<z.ZodEnum<["integrated", "needs_human_input", "repository_checks_failed", "smoke_execution_failed", "nothing_to_integrate", "failed"]>, "failed" | "integrated" | "needs_human_input" | "repository_checks_failed" | "smoke_execution_failed" | "nothing_to_integrate", unknown>;
|
|
9
|
+
summary: z.ZodEffects<z.ZodString, string, unknown>;
|
|
10
|
+
skills_consulted: z.ZodDefault<z.ZodEffects<z.ZodArray<z.ZodEffects<z.ZodString, string, unknown>, "many">, string[], unknown>>;
|
|
11
|
+
skills_used: z.ZodDefault<z.ZodEffects<z.ZodArray<z.ZodEffects<z.ZodString, string, unknown>, "many">, string[], unknown>>;
|
|
12
|
+
packages_installed: z.ZodDefault<z.ZodEffects<z.ZodArray<z.ZodEffects<z.ZodString, string, unknown>, "many">, string[], unknown>>;
|
|
13
|
+
files_changed: z.ZodDefault<z.ZodEffects<z.ZodArray<z.ZodEffects<z.ZodString, string, unknown>, "many">, string[], unknown>>;
|
|
14
|
+
call_sites_instrumented: z.ZodDefault<z.ZodEffects<z.ZodArray<z.ZodUnion<[z.ZodEffects<z.ZodEffects<z.ZodString, string, unknown>, {
|
|
15
|
+
file: string;
|
|
16
|
+
}, unknown>, z.ZodObject<{
|
|
17
|
+
file: z.ZodEffects<z.ZodString, string, unknown>;
|
|
18
|
+
detail: z.ZodEffects<z.ZodOptional<z.ZodString>, string | undefined, unknown>;
|
|
19
|
+
}, "strip", z.ZodTypeAny, {
|
|
20
|
+
file: string;
|
|
21
|
+
detail?: string | undefined;
|
|
22
|
+
}, {
|
|
23
|
+
file?: unknown;
|
|
24
|
+
detail?: unknown;
|
|
25
|
+
}>]>, "many">, ({
|
|
26
|
+
file: string;
|
|
27
|
+
} | {
|
|
28
|
+
file: string;
|
|
29
|
+
detail?: string | undefined;
|
|
30
|
+
})[], unknown>>;
|
|
31
|
+
commands_run: z.ZodDefault<z.ZodEffects<z.ZodArray<z.ZodObject<{
|
|
32
|
+
command: z.ZodEffects<z.ZodString, string, unknown>;
|
|
33
|
+
exit_code: z.ZodEffects<z.ZodUnion<[z.ZodNumber, z.ZodNull]>, number | null, unknown>;
|
|
34
|
+
kind: z.ZodEffects<z.ZodOptional<z.ZodString>, string | undefined, unknown>;
|
|
35
|
+
}, "strip", z.ZodTypeAny, {
|
|
36
|
+
command: string;
|
|
37
|
+
exit_code: number | null;
|
|
38
|
+
kind?: string | undefined;
|
|
39
|
+
}, {
|
|
40
|
+
command?: unknown;
|
|
41
|
+
kind?: unknown;
|
|
42
|
+
exit_code?: unknown;
|
|
43
|
+
}>, "many">, {
|
|
44
|
+
command: string;
|
|
45
|
+
exit_code: number | null;
|
|
46
|
+
kind?: string | undefined;
|
|
47
|
+
}[], unknown>>;
|
|
48
|
+
smoke: z.ZodDefault<z.ZodEffects<z.ZodNullable<z.ZodObject<{
|
|
49
|
+
method: z.ZodEffects<z.ZodString, string, unknown>;
|
|
50
|
+
command: z.ZodEffects<z.ZodOptional<z.ZodString>, string | undefined, unknown>;
|
|
51
|
+
verification_id: z.ZodEffects<z.ZodString, string, unknown>;
|
|
52
|
+
}, "strip", z.ZodTypeAny, {
|
|
53
|
+
method: string;
|
|
54
|
+
verification_id: string;
|
|
55
|
+
command?: string | undefined;
|
|
56
|
+
}, {
|
|
57
|
+
command?: unknown;
|
|
58
|
+
method?: unknown;
|
|
59
|
+
verification_id?: unknown;
|
|
60
|
+
}>>, {
|
|
61
|
+
method: string;
|
|
62
|
+
verification_id: string;
|
|
63
|
+
command?: string | undefined;
|
|
64
|
+
} | null, unknown>>;
|
|
65
|
+
remaining_actions: z.ZodDefault<z.ZodEffects<z.ZodArray<z.ZodEffects<z.ZodString, string, unknown>, "many">, string[], unknown>>;
|
|
66
|
+
failure: z.ZodDefault<z.ZodEffects<z.ZodNullable<z.ZodObject<{
|
|
67
|
+
class: z.ZodEffects<z.ZodString, string, unknown>;
|
|
68
|
+
detail: z.ZodEffects<z.ZodString, string, unknown>;
|
|
69
|
+
}, "strip", z.ZodTypeAny, {
|
|
70
|
+
detail: string;
|
|
71
|
+
class: string;
|
|
72
|
+
}, {
|
|
73
|
+
detail?: unknown;
|
|
74
|
+
class?: unknown;
|
|
75
|
+
}>>, {
|
|
76
|
+
detail: string;
|
|
77
|
+
class: string;
|
|
78
|
+
} | null, unknown>>;
|
|
79
|
+
}, "strip", z.ZodTypeAny, {
|
|
80
|
+
schema_version: "moda.sdk_integration_result.v1";
|
|
81
|
+
summary: string;
|
|
82
|
+
status: "failed" | "integrated" | "needs_human_input" | "repository_checks_failed" | "smoke_execution_failed" | "nothing_to_integrate";
|
|
83
|
+
skills_consulted: string[];
|
|
84
|
+
skills_used: string[];
|
|
85
|
+
packages_installed: string[];
|
|
86
|
+
files_changed: string[];
|
|
87
|
+
call_sites_instrumented: ({
|
|
88
|
+
file: string;
|
|
89
|
+
} | {
|
|
90
|
+
file: string;
|
|
91
|
+
detail?: string | undefined;
|
|
92
|
+
})[];
|
|
93
|
+
commands_run: {
|
|
94
|
+
command: string;
|
|
95
|
+
exit_code: number | null;
|
|
96
|
+
kind?: string | undefined;
|
|
97
|
+
}[];
|
|
98
|
+
smoke: {
|
|
99
|
+
method: string;
|
|
100
|
+
verification_id: string;
|
|
101
|
+
command?: string | undefined;
|
|
102
|
+
} | null;
|
|
103
|
+
remaining_actions: string[];
|
|
104
|
+
failure: {
|
|
105
|
+
detail: string;
|
|
106
|
+
class: string;
|
|
107
|
+
} | null;
|
|
108
|
+
}, {
|
|
109
|
+
schema_version: string;
|
|
110
|
+
summary?: unknown;
|
|
111
|
+
status?: unknown;
|
|
112
|
+
skills_consulted?: unknown;
|
|
113
|
+
skills_used?: unknown;
|
|
114
|
+
packages_installed?: unknown;
|
|
115
|
+
files_changed?: unknown;
|
|
116
|
+
call_sites_instrumented?: unknown;
|
|
117
|
+
commands_run?: unknown;
|
|
118
|
+
smoke?: unknown;
|
|
119
|
+
remaining_actions?: unknown;
|
|
120
|
+
failure?: unknown;
|
|
121
|
+
}>;
|
|
122
|
+
export type SdkAgentResult = z.infer<typeof SdkAgentResultSchema>;
|
|
123
|
+
export type ExtractSdkAgentResult = {
|
|
124
|
+
result: SdkAgentResult;
|
|
125
|
+
source: 'file' | 'markers';
|
|
126
|
+
} | {
|
|
127
|
+
error: string;
|
|
128
|
+
detail?: string;
|
|
129
|
+
};
|
|
130
|
+
export declare function extractSdkAgentResult(opts: {
|
|
131
|
+
cwd: string;
|
|
132
|
+
stdout: string;
|
|
133
|
+
stderr: string;
|
|
134
|
+
}): ExtractSdkAgentResult;
|
|
135
|
+
//# sourceMappingURL=sdk-protocol.d.ts.map
|
|
@@ -0,0 +1,152 @@
|
|
|
1
|
+
// src/init/sdk-protocol.ts — Versioned result protocol used by the coding
|
|
2
|
+
// agent that performs SDK integration.
|
|
3
|
+
import { existsSync, readFileSync } from 'node:fs';
|
|
4
|
+
import { join } from 'node:path';
|
|
5
|
+
import { z } from 'zod';
|
|
6
|
+
export const SDK_RESULT_SCHEMA_VERSION = 'moda.sdk_integration_result.v1';
|
|
7
|
+
export const SDK_RESULT_JSON_BEGIN = 'MODA_SDK_RESULT_JSON_BEGIN';
|
|
8
|
+
export const SDK_RESULT_JSON_END = 'MODA_SDK_RESULT_JSON_END';
|
|
9
|
+
export const SDK_RESULT_FILE = '.moda/sdk-integration-result.json';
|
|
10
|
+
const nonemptyString = z.preprocess((value) => {
|
|
11
|
+
if (typeof value === 'string')
|
|
12
|
+
return value.trim();
|
|
13
|
+
if (typeof value === 'number' || typeof value === 'boolean' || typeof value === 'bigint') {
|
|
14
|
+
return String(value);
|
|
15
|
+
}
|
|
16
|
+
return value;
|
|
17
|
+
}, z.string().min(1));
|
|
18
|
+
const optionalString = z.preprocess((value) => {
|
|
19
|
+
if (value === undefined)
|
|
20
|
+
return undefined;
|
|
21
|
+
if (typeof value === 'string') {
|
|
22
|
+
const trimmed = value.trim();
|
|
23
|
+
return trimmed || undefined;
|
|
24
|
+
}
|
|
25
|
+
if (typeof value === 'number' || typeof value === 'boolean' || typeof value === 'bigint') {
|
|
26
|
+
return String(value);
|
|
27
|
+
}
|
|
28
|
+
return undefined;
|
|
29
|
+
}, z.string().min(1).optional());
|
|
30
|
+
function tolerantArray(itemSchema) {
|
|
31
|
+
return z.preprocess((value) => {
|
|
32
|
+
if (!Array.isArray(value))
|
|
33
|
+
return [];
|
|
34
|
+
const accepted = [];
|
|
35
|
+
for (const item of value) {
|
|
36
|
+
const parsed = itemSchema.safeParse(item);
|
|
37
|
+
if (parsed.success)
|
|
38
|
+
accepted.push(parsed.data);
|
|
39
|
+
}
|
|
40
|
+
return accepted;
|
|
41
|
+
}, z.array(itemSchema)).default([]);
|
|
42
|
+
}
|
|
43
|
+
const callSiteSchema = z.union([
|
|
44
|
+
nonemptyString.transform((file) => ({ file })),
|
|
45
|
+
z.object({
|
|
46
|
+
file: nonemptyString,
|
|
47
|
+
detail: optionalString,
|
|
48
|
+
}),
|
|
49
|
+
]);
|
|
50
|
+
const exitCodeSchema = z.preprocess((value) => {
|
|
51
|
+
if (value === null)
|
|
52
|
+
return null;
|
|
53
|
+
if (typeof value === 'string' && value.trim() !== '') {
|
|
54
|
+
const numeric = Number(value);
|
|
55
|
+
return Number.isFinite(numeric) ? numeric : value;
|
|
56
|
+
}
|
|
57
|
+
return value;
|
|
58
|
+
}, z.union([z.number().finite(), z.null()]));
|
|
59
|
+
const commandSchema = z.object({
|
|
60
|
+
command: nonemptyString,
|
|
61
|
+
exit_code: exitCodeSchema,
|
|
62
|
+
kind: optionalString,
|
|
63
|
+
});
|
|
64
|
+
const smokeSchema = z.object({
|
|
65
|
+
method: nonemptyString,
|
|
66
|
+
command: optionalString,
|
|
67
|
+
verification_id: nonemptyString,
|
|
68
|
+
});
|
|
69
|
+
const failureSchema = z.object({
|
|
70
|
+
class: nonemptyString,
|
|
71
|
+
detail: nonemptyString,
|
|
72
|
+
});
|
|
73
|
+
export const SdkAgentResultSchema = z.object({
|
|
74
|
+
schema_version: z.string().trim().pipe(z.literal(SDK_RESULT_SCHEMA_VERSION)),
|
|
75
|
+
status: z.preprocess((value) => typeof value === 'string' ? value.trim() : value, z.enum([
|
|
76
|
+
'integrated',
|
|
77
|
+
'needs_human_input',
|
|
78
|
+
'repository_checks_failed',
|
|
79
|
+
'smoke_execution_failed',
|
|
80
|
+
'nothing_to_integrate',
|
|
81
|
+
'failed',
|
|
82
|
+
])),
|
|
83
|
+
summary: nonemptyString,
|
|
84
|
+
skills_consulted: tolerantArray(nonemptyString),
|
|
85
|
+
skills_used: tolerantArray(nonemptyString),
|
|
86
|
+
packages_installed: tolerantArray(nonemptyString),
|
|
87
|
+
files_changed: tolerantArray(nonemptyString),
|
|
88
|
+
call_sites_instrumented: tolerantArray(callSiteSchema),
|
|
89
|
+
commands_run: tolerantArray(commandSchema),
|
|
90
|
+
smoke: z.preprocess((value) => value ?? null, smokeSchema.nullable()).default(null),
|
|
91
|
+
remaining_actions: tolerantArray(nonemptyString),
|
|
92
|
+
failure: z.preprocess((value) => value ?? null, failureSchema.nullable()).default(null),
|
|
93
|
+
});
|
|
94
|
+
export function extractSdkAgentResult(opts) {
|
|
95
|
+
const diagnostics = [];
|
|
96
|
+
const resultPath = join(opts.cwd, SDK_RESULT_FILE);
|
|
97
|
+
if (existsSync(resultPath)) {
|
|
98
|
+
try {
|
|
99
|
+
const parsed = parseCandidate(readFileSync(resultPath, 'utf8'));
|
|
100
|
+
if (parsed.result)
|
|
101
|
+
return { result: parsed.result, source: 'file' };
|
|
102
|
+
diagnostics.push(`result file: ${parsed.detail}`);
|
|
103
|
+
}
|
|
104
|
+
catch (error) {
|
|
105
|
+
diagnostics.push(`result file: ${errorMessage(error)}`);
|
|
106
|
+
}
|
|
107
|
+
}
|
|
108
|
+
const output = [opts.stdout, opts.stderr].filter(Boolean).join('\n');
|
|
109
|
+
const candidates = markerCandidates(output);
|
|
110
|
+
for (const [index, candidate] of candidates.entries()) {
|
|
111
|
+
const parsed = parseCandidate(candidate);
|
|
112
|
+
if (parsed.result)
|
|
113
|
+
return { result: parsed.result, source: 'markers' };
|
|
114
|
+
diagnostics.push(`marker block ${index + 1}: ${parsed.detail}`);
|
|
115
|
+
}
|
|
116
|
+
if (candidates.length === 0) {
|
|
117
|
+
diagnostics.push(`no ${SDK_RESULT_JSON_BEGIN}/${SDK_RESULT_JSON_END} marker block found`);
|
|
118
|
+
}
|
|
119
|
+
return {
|
|
120
|
+
error: 'SDK integration result protocol violation.',
|
|
121
|
+
detail: diagnostics.join('; '),
|
|
122
|
+
};
|
|
123
|
+
}
|
|
124
|
+
function markerCandidates(output) {
|
|
125
|
+
const pattern = new RegExp(`${SDK_RESULT_JSON_BEGIN}\\s*([\\s\\S]*?)\\s*${SDK_RESULT_JSON_END}`, 'g');
|
|
126
|
+
const candidates = [];
|
|
127
|
+
for (const match of output.matchAll(pattern)) {
|
|
128
|
+
if (match[1])
|
|
129
|
+
candidates.push(match[1]);
|
|
130
|
+
}
|
|
131
|
+
return candidates;
|
|
132
|
+
}
|
|
133
|
+
function parseCandidate(candidate) {
|
|
134
|
+
let value;
|
|
135
|
+
try {
|
|
136
|
+
value = JSON.parse(candidate.trim());
|
|
137
|
+
}
|
|
138
|
+
catch (error) {
|
|
139
|
+
return { detail: `invalid JSON (${errorMessage(error)})` };
|
|
140
|
+
}
|
|
141
|
+
const parsed = SdkAgentResultSchema.safeParse(value);
|
|
142
|
+
if (parsed.success)
|
|
143
|
+
return { result: parsed.data };
|
|
144
|
+
const detail = parsed.error.issues
|
|
145
|
+
.map((issue) => `${issue.path.join('.') || '<root>'}: ${issue.message}`)
|
|
146
|
+
.join(', ');
|
|
147
|
+
return { detail: `validation failed (${detail})` };
|
|
148
|
+
}
|
|
149
|
+
function errorMessage(error) {
|
|
150
|
+
return error instanceof Error ? error.message : String(error);
|
|
151
|
+
}
|
|
152
|
+
//# sourceMappingURL=sdk-protocol.js.map
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
export interface VerificationEvidence {
|
|
2
|
+
observed: boolean;
|
|
3
|
+
verification_id: string;
|
|
4
|
+
total_spans: number;
|
|
5
|
+
llm_spans: number;
|
|
6
|
+
tool_spans: number;
|
|
7
|
+
trace_ids: string[];
|
|
8
|
+
orphan_count: number;
|
|
9
|
+
duplicate_count: number;
|
|
10
|
+
ingest_rows: number;
|
|
11
|
+
elapsed_ms: number;
|
|
12
|
+
polls: number;
|
|
13
|
+
last_error?: string;
|
|
14
|
+
}
|
|
15
|
+
export declare function generateVerificationId(): string;
|
|
16
|
+
export declare function isModaVerificationId(value: string): boolean;
|
|
17
|
+
export declare function verifyExactTrace(opts: {
|
|
18
|
+
verificationId: string;
|
|
19
|
+
timeoutMs?: number;
|
|
20
|
+
pollIntervalMs?: number;
|
|
21
|
+
fetchAudit?: (id: string) => Promise<unknown>;
|
|
22
|
+
sleep?: (ms: number) => Promise<void>;
|
|
23
|
+
now?: () => number;
|
|
24
|
+
}): Promise<VerificationEvidence>;
|
|
25
|
+
/** Fetch audit data for SDK verification using an explicit provisioned API key. */
|
|
26
|
+
export declare function fetchAuditWithApiKey(id: string, apiKey: string, baseUrl: string): Promise<unknown>;
|
|
27
|
+
//# sourceMappingURL=sdk-verification.d.ts.map
|
|
@@ -0,0 +1,123 @@
|
|
|
1
|
+
// src/init/sdk-verification.ts — Poll the Data API for an exact, freshly
|
|
2
|
+
// generated conversation id and require evidence of an observed LLM span.
|
|
3
|
+
import { randomBytes } from 'node:crypto';
|
|
4
|
+
const DEFAULT_TIMEOUT_MS = 90_000;
|
|
5
|
+
const DEFAULT_POLL_INTERVAL_MS = 3_000;
|
|
6
|
+
const MIN_TIMEOUT_MS = 10_000;
|
|
7
|
+
const MAX_TIMEOUT_MS = 600_000;
|
|
8
|
+
export function generateVerificationId() {
|
|
9
|
+
return `moda-verify-${randomBytes(16).toString('hex')}`;
|
|
10
|
+
}
|
|
11
|
+
export function isModaVerificationId(value) {
|
|
12
|
+
return /^moda-verify-[0-9a-f]{32}$/.test(value);
|
|
13
|
+
}
|
|
14
|
+
export async function verifyExactTrace(opts) {
|
|
15
|
+
const now = opts.now ?? Date.now;
|
|
16
|
+
const sleep = opts.sleep ?? ((ms) => new Promise((resolve) => setTimeout(resolve, ms)));
|
|
17
|
+
// Verification MUST poll with the provisioned key (fetchAuditWithApiKey) so an
|
|
18
|
+
// ambient MODA_API_KEY pointing at another tenant cannot skew the result.
|
|
19
|
+
// Fail explicitly rather than silently falling back to ambient credentials.
|
|
20
|
+
const fetchAudit = opts.fetchAudit;
|
|
21
|
+
const timeoutMs = opts.timeoutMs === undefined || !Number.isFinite(opts.timeoutMs)
|
|
22
|
+
? resolveTimeoutFromEnv()
|
|
23
|
+
: Math.max(0, opts.timeoutMs);
|
|
24
|
+
const requestedPollInterval = opts.pollIntervalMs ?? DEFAULT_POLL_INTERVAL_MS;
|
|
25
|
+
const pollIntervalMs = Number.isFinite(requestedPollInterval)
|
|
26
|
+
? Math.max(1, requestedPollInterval)
|
|
27
|
+
: DEFAULT_POLL_INTERVAL_MS;
|
|
28
|
+
const startedAt = now();
|
|
29
|
+
const deadline = startedAt + Math.max(0, timeoutMs);
|
|
30
|
+
let polls = 0;
|
|
31
|
+
let lastError;
|
|
32
|
+
let counts = {
|
|
33
|
+
total_spans: 0,
|
|
34
|
+
llm_spans: 0,
|
|
35
|
+
tool_spans: 0,
|
|
36
|
+
trace_ids: [],
|
|
37
|
+
orphan_count: 0,
|
|
38
|
+
duplicate_count: 0,
|
|
39
|
+
ingest_rows: 0,
|
|
40
|
+
};
|
|
41
|
+
if (!fetchAudit) {
|
|
42
|
+
lastError = 'verification requires an explicit audit fetcher bound to the provisioned API key';
|
|
43
|
+
return buildEvidence(false);
|
|
44
|
+
}
|
|
45
|
+
while (true) {
|
|
46
|
+
polls++;
|
|
47
|
+
try {
|
|
48
|
+
const response = await fetchAudit(opts.verificationId);
|
|
49
|
+
const parsed = readAuditCounts(response);
|
|
50
|
+
if (parsed) {
|
|
51
|
+
counts = parsed;
|
|
52
|
+
if (counts.total_spans >= 1 && counts.llm_spans >= 1) {
|
|
53
|
+
return buildEvidence(true);
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
catch (error) {
|
|
58
|
+
lastError = error instanceof Error ? error.message : String(error);
|
|
59
|
+
}
|
|
60
|
+
const currentTime = now();
|
|
61
|
+
if (currentTime >= deadline)
|
|
62
|
+
return buildEvidence(false);
|
|
63
|
+
await sleep(Math.min(pollIntervalMs, deadline - currentTime));
|
|
64
|
+
}
|
|
65
|
+
function buildEvidence(observed) {
|
|
66
|
+
return {
|
|
67
|
+
observed,
|
|
68
|
+
verification_id: opts.verificationId,
|
|
69
|
+
...counts,
|
|
70
|
+
elapsed_ms: Math.max(0, now() - startedAt),
|
|
71
|
+
polls,
|
|
72
|
+
...(lastError ? { last_error: lastError } : {}),
|
|
73
|
+
};
|
|
74
|
+
}
|
|
75
|
+
}
|
|
76
|
+
function resolveTimeoutFromEnv() {
|
|
77
|
+
const raw = process.env.MODA_SDK_VERIFY_TIMEOUT_MS;
|
|
78
|
+
if (!raw)
|
|
79
|
+
return DEFAULT_TIMEOUT_MS;
|
|
80
|
+
const parsed = Number(raw);
|
|
81
|
+
if (!Number.isFinite(parsed))
|
|
82
|
+
return DEFAULT_TIMEOUT_MS;
|
|
83
|
+
return Math.min(MAX_TIMEOUT_MS, Math.max(MIN_TIMEOUT_MS, Math.trunc(parsed)));
|
|
84
|
+
}
|
|
85
|
+
function readAuditCounts(value) {
|
|
86
|
+
if (!isRecord(value) || !isRecord(value.summary))
|
|
87
|
+
return undefined;
|
|
88
|
+
const summary = value.summary;
|
|
89
|
+
if (!isNonNegativeNumber(summary.total_spans) || !isRecord(summary.by_type))
|
|
90
|
+
return undefined;
|
|
91
|
+
return {
|
|
92
|
+
total_spans: summary.total_spans,
|
|
93
|
+
llm_spans: isNonNegativeNumber(summary.by_type.llm) ? summary.by_type.llm : 0,
|
|
94
|
+
tool_spans: isNonNegativeNumber(summary.by_type.tool) ? summary.by_type.tool : 0,
|
|
95
|
+
trace_ids: Array.isArray(summary.trace_ids)
|
|
96
|
+
? summary.trace_ids.filter((item) => typeof item === 'string')
|
|
97
|
+
: [],
|
|
98
|
+
orphan_count: isNonNegativeNumber(summary.orphan_count) ? summary.orphan_count : 0,
|
|
99
|
+
duplicate_count: isNonNegativeNumber(summary.duplicate_count) ? summary.duplicate_count : 0,
|
|
100
|
+
ingest_rows: isNonNegativeNumber(summary.ingest_rows) ? summary.ingest_rows : 0,
|
|
101
|
+
};
|
|
102
|
+
}
|
|
103
|
+
function isRecord(value) {
|
|
104
|
+
return typeof value === 'object' && value !== null && !Array.isArray(value);
|
|
105
|
+
}
|
|
106
|
+
function isNonNegativeNumber(value) {
|
|
107
|
+
return typeof value === 'number' && Number.isFinite(value) && value >= 0;
|
|
108
|
+
}
|
|
109
|
+
/** Fetch audit data for SDK verification using an explicit provisioned API key. */
|
|
110
|
+
export async function fetchAuditWithApiKey(id, apiKey, baseUrl) {
|
|
111
|
+
const url = `${baseUrl}/api/v1/data/audit/${encodeURIComponent(id)}?kind=conversation`;
|
|
112
|
+
const response = await fetch(url, {
|
|
113
|
+
method: 'GET',
|
|
114
|
+
headers: { 'x-api-key': apiKey },
|
|
115
|
+
signal: AbortSignal.timeout(10_000),
|
|
116
|
+
});
|
|
117
|
+
const body = await response.text();
|
|
118
|
+
if (!response.ok) {
|
|
119
|
+
throw new Error(`Audit fetch failed (${response.status}): ${body.slice(0, 200)}`);
|
|
120
|
+
}
|
|
121
|
+
return JSON.parse(body);
|
|
122
|
+
}
|
|
123
|
+
//# sourceMappingURL=sdk-verification.js.map
|
package/dist/sync.js
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { CliInputError } from './errors.js';
|
|
2
2
|
import { runHarnessCommand } from './harness.js';
|
|
3
3
|
import { runPromptsCommand } from './prompts.js';
|
|
4
|
+
import { runSkillsCommand } from './skills.js';
|
|
4
5
|
export async function runSyncCommand(context) {
|
|
5
6
|
const resource = context.positionals[0] ?? 'harness';
|
|
6
7
|
const rest = context.positionals.slice(1);
|
|
@@ -20,8 +21,16 @@ export async function runSyncCommand(context) {
|
|
|
20
21
|
return { exitCode: 0 };
|
|
21
22
|
}
|
|
22
23
|
if (resource === 'skills') {
|
|
23
|
-
|
|
24
|
+
// Skills surface is pull-oriented (install approved skills locally), unlike
|
|
25
|
+
// prompts sync which pushes authored config upstream.
|
|
26
|
+
const exitCode = await runSkillsCommand({
|
|
27
|
+
...context,
|
|
28
|
+
command: 'skills',
|
|
29
|
+
positional: 'pull',
|
|
30
|
+
positionals: ['pull', ...rest],
|
|
31
|
+
});
|
|
32
|
+
return { exitCode };
|
|
24
33
|
}
|
|
25
|
-
throw new CliInputError(`Unknown sync resource '${resource}'.`, 'Usage: moda sync [harness|prompts] [--dry-run] [--profile=NAME]');
|
|
34
|
+
throw new CliInputError(`Unknown sync resource '${resource}'.`, 'Usage: moda sync [harness|prompts|skills] [--dry-run] [--profile=NAME]');
|
|
26
35
|
}
|
|
27
36
|
//# sourceMappingURL=sync.js.map
|
package/package.json
CHANGED
|
@@ -1,19 +0,0 @@
|
|
|
1
|
-
import type { SdkGuideContext } from './sdk-guide.js';
|
|
2
|
-
type Language = 'node' | 'python';
|
|
3
|
-
type Framework = 'vercel-ai-sdk' | 'langchain' | 'claude-agent-sdk';
|
|
4
|
-
type Provider = 'openai' | 'anthropic';
|
|
5
|
-
interface ResolutionRow {
|
|
6
|
-
language: Language;
|
|
7
|
-
framework?: Framework;
|
|
8
|
-
provider?: Provider;
|
|
9
|
-
skillId: string;
|
|
10
|
-
}
|
|
11
|
-
/** The §2.3 resolution table, encoded as data. Order matters. */
|
|
12
|
-
export declare const RESOLUTION_TABLE: readonly ResolutionRow[];
|
|
13
|
-
/**
|
|
14
|
-
* Resolve the detected context to a bundled skill id, or null when no
|
|
15
|
-
* language was detected (callers fall back to the slim generic guide).
|
|
16
|
-
*/
|
|
17
|
-
export declare function resolveIntegrationSkillId(context: SdkGuideContext): string | null;
|
|
18
|
-
export {};
|
|
19
|
-
//# sourceMappingURL=skill-resolver.d.ts.map
|
|
@@ -1,78 +0,0 @@
|
|
|
1
|
-
// src/init/skill-resolver.ts — deterministic resolution from detected project
|
|
2
|
-
// context to a bundled integration-skill id.
|
|
3
|
-
//
|
|
4
|
-
// Pure function, zero LLM involvement. The table is ordered; the first
|
|
5
|
-
// matching row wins. Wrapper frameworks beat raw providers: if the Vercel AI
|
|
6
|
-
// SDK or LangChain is present, the wrapper layer gets instrumented, never the
|
|
7
|
-
// underlying openai/anthropic client.
|
|
8
|
-
/** The §2.3 resolution table, encoded as data. Order matters. */
|
|
9
|
-
export const RESOLUTION_TABLE = [
|
|
10
|
-
{ language: 'node', framework: 'vercel-ai-sdk', skillId: 'integration-node-vercel-ai-sdk' },
|
|
11
|
-
{ language: 'node', framework: 'langchain', skillId: 'integration-node-langchain' },
|
|
12
|
-
{ language: 'python', framework: 'langchain', skillId: 'integration-python-langchain' },
|
|
13
|
-
{ language: 'python', framework: 'claude-agent-sdk', skillId: 'integration-python-claude-agent-sdk' },
|
|
14
|
-
{ language: 'node', provider: 'openai', skillId: 'integration-node-openai' },
|
|
15
|
-
{ language: 'node', provider: 'anthropic', skillId: 'integration-node-anthropic' },
|
|
16
|
-
{ language: 'python', provider: 'openai', skillId: 'integration-python-openai' },
|
|
17
|
-
{ language: 'python', provider: 'anthropic', skillId: 'integration-python-anthropic' },
|
|
18
|
-
{ language: 'node', skillId: 'integration-node-generic' },
|
|
19
|
-
{ language: 'python', skillId: 'integration-python-generic' },
|
|
20
|
-
];
|
|
21
|
-
/**
|
|
22
|
-
* Resolve the detected context to a bundled skill id, or null when no
|
|
23
|
-
* language was detected (callers fall back to the slim generic guide).
|
|
24
|
-
*/
|
|
25
|
-
export function resolveIntegrationSkillId(context) {
|
|
26
|
-
const languages = normalizeLanguages(context.languages);
|
|
27
|
-
const frameworks = normalizeFrameworks(context.frameworkHints);
|
|
28
|
-
const providers = normalizeProviders(context.providerHints);
|
|
29
|
-
for (const row of RESOLUTION_TABLE) {
|
|
30
|
-
if (!languages.has(row.language))
|
|
31
|
-
continue;
|
|
32
|
-
if (row.framework && !frameworks.has(row.framework))
|
|
33
|
-
continue;
|
|
34
|
-
if (row.provider && !providers.has(row.provider))
|
|
35
|
-
continue;
|
|
36
|
-
return row.skillId;
|
|
37
|
-
}
|
|
38
|
-
return null;
|
|
39
|
-
}
|
|
40
|
-
function normalizeLanguages(languages) {
|
|
41
|
-
const result = new Set();
|
|
42
|
-
for (const language of languages) {
|
|
43
|
-
const lower = language.toLowerCase();
|
|
44
|
-
if (lower.includes('node') || lower.includes('typescript') || lower.includes('javascript')) {
|
|
45
|
-
result.add('node');
|
|
46
|
-
}
|
|
47
|
-
if (lower.includes('python'))
|
|
48
|
-
result.add('python');
|
|
49
|
-
}
|
|
50
|
-
return result;
|
|
51
|
-
}
|
|
52
|
-
function normalizeFrameworks(frameworkHints) {
|
|
53
|
-
const result = new Set();
|
|
54
|
-
for (const hint of frameworkHints) {
|
|
55
|
-
const lower = hint.toLowerCase();
|
|
56
|
-
if (lower.includes('vercel ai sdk') || lower.includes('vercel-ai-sdk') || lower.includes('ai sdk')) {
|
|
57
|
-
result.add('vercel-ai-sdk');
|
|
58
|
-
}
|
|
59
|
-
if (lower.includes('langchain') || lower.includes('langgraph'))
|
|
60
|
-
result.add('langchain');
|
|
61
|
-
if (lower.includes('claude agent sdk') || lower.includes('claude-agent-sdk')) {
|
|
62
|
-
result.add('claude-agent-sdk');
|
|
63
|
-
}
|
|
64
|
-
}
|
|
65
|
-
return result;
|
|
66
|
-
}
|
|
67
|
-
function normalizeProviders(providerHints) {
|
|
68
|
-
const result = new Set();
|
|
69
|
-
for (const hint of providerHints) {
|
|
70
|
-
const lower = hint.toLowerCase();
|
|
71
|
-
if (lower.includes('openai'))
|
|
72
|
-
result.add('openai');
|
|
73
|
-
if (lower.includes('anthropic'))
|
|
74
|
-
result.add('anthropic');
|
|
75
|
-
}
|
|
76
|
-
return result;
|
|
77
|
-
}
|
|
78
|
-
//# sourceMappingURL=skill-resolver.js.map
|