@superpanel-sdk/runtime 0.2.3 → 0.7.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/README.md +30 -10
- package/dist/builder/codegen/emit.d.ts +25 -12
- package/dist/builder/codegen/emit.js +339 -78
- package/dist/builder/codegen/generate.d.ts +21 -2
- package/dist/builder/codegen/generate.js +147 -34
- package/dist/builder/company-import-graph.d.ts +2 -0
- package/dist/builder/company-import-graph.js +40 -8
- package/dist/builder/company-imports.d.ts +29 -0
- package/dist/builder/company-imports.js +122 -0
- package/dist/builder/compile/compile-sequence.d.ts +61 -8
- package/dist/builder/compile/compile-sequence.js +334 -80
- package/dist/builder/compile/function-code.d.ts +19 -3
- package/dist/builder/compile/function-code.js +123 -45
- package/dist/builder/compile/registry.d.ts +7 -3
- package/dist/builder/compile/registry.js +30 -27
- package/dist/builder/errors.d.ts +9 -0
- package/dist/builder/errors.js +17 -0
- package/dist/builder/function-params.d.ts +10 -0
- package/dist/builder/function-params.js +203 -0
- package/dist/builder/load.d.ts +70 -10
- package/dist/builder/load.js +258 -67
- package/dist/builder/paths.d.ts +9 -1
- package/dist/builder/paths.js +32 -2
- package/dist/builder/schedules.d.ts +25 -0
- package/dist/builder/schedules.js +101 -0
- package/dist/builder/slot-schema-validation.d.ts +4 -0
- package/dist/builder/slot-schema-validation.js +10 -0
- package/dist/builder/typecheck.d.ts +8 -0
- package/dist/builder/typecheck.js +102 -0
- package/dist/builder/types.d.ts +127 -19
- package/dist/builder/types.js +21 -1
- package/dist/cli/auth/auth.d.ts +37 -0
- package/dist/cli/auth/auth.js +211 -0
- package/dist/cli/auth/backend-url.d.ts +5 -0
- package/dist/cli/auth/backend-url.js +25 -0
- package/dist/cli/auth/clipboard.d.ts +9 -0
- package/dist/cli/auth/clipboard.js +38 -0
- package/dist/cli/auth/http.d.ts +20 -0
- package/dist/cli/auth/http.js +27 -0
- package/dist/cli/auth/index.d.ts +10 -0
- package/dist/cli/auth/index.js +27 -0
- package/dist/cli/auth/progress.d.ts +12 -0
- package/dist/cli/auth/progress.js +15 -0
- package/dist/cli/auth/serve-login.d.ts +8 -0
- package/dist/cli/auth/serve-login.js +24 -0
- package/dist/cli/calls/index.d.ts +177 -0
- package/dist/cli/calls/index.js +96 -0
- package/dist/cli/compile/index.js +3 -0
- package/dist/cli/config.d.ts +10 -0
- package/dist/cli/config.js +52 -0
- package/dist/cli/debug/index.d.ts +31 -0
- package/dist/cli/debug/index.js +119 -0
- package/dist/cli/exec/index.d.ts +39 -0
- package/dist/cli/exec/index.js +138 -0
- package/dist/cli/exec/session.d.ts +48 -0
- package/dist/cli/exec/session.js +82 -0
- package/dist/cli/exec/trace.d.ts +18 -0
- package/dist/cli/exec/trace.js +52 -0
- package/dist/cli/index.js +112 -9
- package/dist/cli/logs/index.d.ts +47 -0
- package/dist/cli/logs/index.js +113 -0
- package/dist/cli/mobile/index.d.ts +12 -0
- package/dist/cli/mobile/index.js +405 -0
- package/dist/cli/serve/index.d.ts +109 -3
- package/dist/cli/serve/index.js +506 -118
- package/dist/cli/turns/index.d.ts +105 -0
- package/dist/cli/turns/index.js +128 -0
- package/dist/cli/turns/run.d.ts +13 -0
- package/dist/cli/turns/run.js +57 -0
- package/dist/cli/turns/shadow.d.ts +112 -0
- package/dist/cli/turns/shadow.js +54 -0
- package/dist/cli/type-gen/index.js +9 -9
- package/dist/compiler.d.ts +7 -0
- package/dist/compiler.js +7 -0
- package/dist/compute-host/compute-host.d.ts +14 -23
- package/dist/compute-host/compute-host.js +18 -30
- package/dist/compute-host/index.d.ts +2 -4
- package/dist/compute-host/index.js +1 -2
- package/dist/compute-host/source-compute-host.d.ts +10 -10
- package/dist/compute-host/source-compute-host.js +33 -40
- package/dist/core/case-orchestrator/contact-hours.d.ts +70 -0
- package/dist/core/case-orchestrator/contact-hours.js +127 -0
- package/dist/core/case-orchestrator/index.d.ts +3 -0
- package/dist/core/case-orchestrator/index.js +3 -0
- package/dist/core/case-orchestrator/render.d.ts +75 -0
- package/dist/core/case-orchestrator/render.js +244 -0
- package/dist/core/case-orchestrator/time.d.ts +11 -0
- package/dist/core/case-orchestrator/time.js +29 -0
- package/dist/core/case-orchestrator/types.d.ts +708 -0
- package/dist/core/case-orchestrator/types.js +42 -0
- package/dist/core/context/call.d.ts +291 -0
- package/dist/core/context/call.js +94 -0
- package/dist/core/context/channels.d.ts +60 -0
- package/dist/core/context/channels.js +35 -0
- package/dist/core/context/contact.d.ts +96 -6
- package/dist/core/context/contact.js +48 -2
- package/dist/core/context/email-message.d.ts +198 -0
- package/dist/core/context/email-message.js +86 -0
- package/dist/core/context/email.d.ts +109 -0
- package/dist/core/context/email.js +90 -0
- package/dist/core/context/exchanges.d.ts +167 -0
- package/dist/core/context/exchanges.js +81 -0
- package/dist/core/context/instances.d.ts +307 -0
- package/dist/core/context/instances.js +137 -0
- package/dist/core/context/kv.d.ts +113 -0
- package/dist/core/context/kv.js +60 -0
- package/dist/core/context/platform-registry.d.ts +544 -4
- package/dist/core/context/platform-registry.js +14 -2
- package/dist/core/context/platform.d.ts +2 -1
- package/dist/core/context/sms.d.ts +177 -0
- package/dist/core/context/sms.js +77 -0
- package/dist/core/context/types.d.ts +79 -48
- package/dist/core/context/types.js +1 -7
- package/dist/core/conversation/index.d.ts +85 -0
- package/dist/core/conversation/index.js +133 -0
- package/dist/core/functions/errors.d.ts +74 -0
- package/dist/core/functions/errors.js +26 -0
- package/dist/core/functions/events.d.ts +192 -0
- package/dist/core/functions/events.js +23 -0
- package/dist/core/functions/schedules.d.ts +50 -0
- package/dist/core/functions/schedules.js +20 -0
- package/dist/core/functions/types.d.ts +29 -73
- package/dist/core/functions/types.js +1 -4
- package/dist/core/office-hours/index.d.ts +39 -0
- package/dist/core/office-hours/index.js +187 -0
- package/dist/core/page/define.d.ts +904 -0
- package/dist/core/page/define.js +518 -0
- package/dist/core/page/envelope.d.ts +55 -0
- package/dist/core/page/envelope.js +4 -0
- package/dist/core/page/fns.d.ts +448 -0
- package/dist/core/page/fns.js +539 -0
- package/dist/core/page/memory-reader.d.ts +27 -0
- package/dist/core/page/memory-reader.js +299 -0
- package/dist/core/page/ops.d.ts +58 -0
- package/dist/core/page/ops.js +220 -0
- package/dist/core/page/page-host.d.ts +20 -0
- package/dist/core/page/page-host.js +57 -0
- package/dist/core/page/records-query.d.ts +234 -0
- package/dist/core/page/records-query.js +780 -0
- package/dist/core/page/render.d.ts +295 -0
- package/dist/core/page/render.js +1060 -0
- package/dist/core/page/sandbox.d.ts +85 -0
- package/dist/core/page/sandbox.js +357 -0
- package/dist/core/page/time-format.d.ts +46 -0
- package/dist/core/page/time-format.js +125 -0
- package/dist/core/page/timeline.d.ts +40 -0
- package/dist/core/page/timeline.js +2 -0
- package/dist/core/page/typed.d.ts +648 -0
- package/dist/core/page/typed.js +2 -0
- package/dist/core/page/workspace.d.ts +57 -0
- package/dist/core/page/workspace.js +266 -0
- package/dist/core/party/index.d.ts +3 -0
- package/dist/core/party/index.js +3 -0
- package/dist/core/party/schema.d.ts +208 -0
- package/dist/core/party/schema.js +113 -0
- package/dist/core/party/types.d.ts +35 -0
- package/dist/core/party/types.js +15 -0
- package/dist/core/sequence/define.d.ts +56 -0
- package/dist/core/sequence/define.js +10 -0
- package/dist/core/sequence/index.d.ts +63 -0
- package/dist/core/sequence/index.js +231 -0
- package/dist/core/slot/binding.d.ts +40 -0
- package/dist/core/slot/binding.js +2 -0
- package/dist/core/slot/company-metadata.d.ts +102 -0
- package/dist/core/slot/company-metadata.js +536 -0
- package/dist/core/slot/compile-schema.d.ts +19 -13
- package/dist/core/slot/compile-schema.js +51 -248
- package/dist/core/slot/contact-timezone.d.ts +110 -0
- package/dist/core/slot/contact-timezone.js +186 -0
- package/dist/core/slot/field.d.ts +2 -25
- package/dist/core/slot/field.js +0 -71
- package/dist/core/slot/id-ref.d.ts +17 -2
- package/dist/core/slot/id-ref.js +16 -8
- package/dist/core/slot/index.d.ts +12 -13
- package/dist/core/slot/index.js +11 -12
- package/dist/core/slot/naming.d.ts +15 -0
- package/dist/core/slot/naming.js +96 -0
- package/dist/core/slot/platform-fields.d.ts +119 -0
- package/dist/core/slot/platform-fields.js +121 -0
- package/dist/core/slot/ref.d.ts +16 -0
- package/dist/core/slot/ref.js +14 -0
- package/dist/core/slot/row.d.ts +2 -9
- package/dist/core/slot/row.js +13 -23
- package/dist/core/slot/schema-compat.d.ts +17 -0
- package/dist/core/slot/schema-compat.js +103 -0
- package/dist/core/slot/schema.d.ts +353 -76
- package/dist/core/slot/schema.js +163 -10
- package/dist/core/slot/types.d.ts +30 -103
- package/dist/core/slot/value.d.ts +28 -3
- package/dist/core/slot/value.js +93 -57
- package/dist/dev-overlay/protocol.d.ts +74 -8
- package/dist/fn-context/build-context.d.ts +3 -1
- package/dist/fn-context/build-context.js +26 -10
- package/dist/fn-context/fake/empty-api.js +9 -28
- package/dist/fn-context/fake/index.d.ts +13 -4
- package/dist/fn-context/fake/index.js +29 -2
- package/dist/fn-context/fn-params.d.ts +3 -0
- package/dist/fn-context/fn-params.js +10 -0
- package/dist/fn-context/index.d.ts +5 -5
- package/dist/fn-context/index.js +4 -4
- package/dist/fn-context/log.js +2 -2
- package/dist/fn-context/make-ctx.d.ts +11 -0
- package/dist/fn-context/make-ctx.js +20 -9
- package/dist/fn-context/rpc/case-orchestrator-tools.d.ts +32 -0
- package/dist/fn-context/rpc/case-orchestrator-tools.js +12 -0
- package/dist/fn-context/rpc/errors.d.ts +13 -0
- package/dist/fn-context/rpc/errors.js +22 -0
- package/dist/fn-context/rpc/index.d.ts +4 -2
- package/dist/fn-context/rpc/index.js +4 -2
- package/dist/fn-context/rpc/integration/index.d.ts +15 -5
- package/dist/fn-context/rpc/integration/index.js +1 -1
- package/dist/fn-context/rpc/integration/list-surfaces.d.ts +21 -0
- package/dist/fn-context/rpc/integration/{list-bindings.js → list-surfaces.js} +4 -4
- package/dist/fn-context/rpc/log.d.ts +11 -1
- package/dist/fn-context/rpc/platform.d.ts +13 -3
- package/dist/fn-context/rpc/platform.js +1 -1
- package/dist/fn-context/rpc/records.d.ts +85 -0
- package/dist/fn-context/rpc/records.js +24 -0
- package/dist/fn-context/rpc/trpc-client.d.ts +44 -2
- package/dist/fn-context/rpc/trpc-client.js +222 -12
- package/dist/fn-context/rpc/trpc-init.d.ts +21 -3
- package/dist/fn-context/rpc/trpc-init.js +10 -4
- package/dist/fn-context/rpc/trpc-router-factory.d.ts +111 -7
- package/dist/fn-context/rpc/trpc-router-factory.js +2 -0
- package/dist/fn-context/run-scope.d.ts +4 -0
- package/dist/fn-context/run-scope.js +9 -0
- package/dist/fn-context/types.d.ts +52 -5
- package/dist/index.d.ts +36 -46
- package/dist/index.js +30 -33
- package/dist/logger.d.ts +11 -0
- package/dist/logger.js +49 -0
- package/dist/platform.d.ts +75 -0
- package/dist/platform.js +57 -0
- package/dist/testing-compile-child.d.ts +2 -0
- package/dist/testing-compile-child.js +20 -0
- package/dist/testing.d.ts +368 -0
- package/dist/testing.js +439 -0
- package/dist/voice-service/actions.d.ts +50 -6
- package/dist/voice-service/index.d.ts +2 -0
- package/dist/voice-service/index.js +2 -0
- package/dist/voice-service/protocol.d.ts +604 -0
- package/dist/voice-service/protocol.js +67 -0
- package/dist/voice-service/types.d.ts +55 -8
- package/dist/worker.d.ts +11 -0
- package/dist/worker.js +11 -0
- package/package.json +38 -15
- package/dist/builder/compile/compile-agenda.d.ts +0 -15
- package/dist/builder/compile/compile-agenda.js +0 -64
- package/dist/builder/merge.d.ts +0 -4
- package/dist/builder/merge.js +0 -19
- package/dist/compute-host/read-only-cache.d.ts +0 -20
- package/dist/compute-host/read-only-cache.js +0 -61
- package/dist/core/context/db.d.ts +0 -69
- package/dist/core/context/db.js +0 -44
- package/dist/core/context/docusign.d.ts +0 -134
- package/dist/core/context/docusign.js +0 -108
- package/dist/core/context/filevine.d.ts +0 -323
- package/dist/core/context/filevine.js +0 -254
- package/dist/core/context/http.d.ts +0 -9
- package/dist/core/context/http.js +0 -4
- package/dist/core/context/hubspot.d.ts +0 -250
- package/dist/core/context/hubspot.js +0 -191
- package/dist/core/context/twilio.d.ts +0 -50
- package/dist/core/context/twilio.js +0 -42
- package/dist/core/functions/define/case-context.d.ts +0 -14
- package/dist/core/functions/define/case-context.js +0 -15
- package/dist/core/functions/define/compute.d.ts +0 -7
- package/dist/core/functions/define/compute.js +0 -17
- package/dist/core/functions/define/custom.d.ts +0 -4
- package/dist/core/functions/define/custom.js +0 -8
- package/dist/core/functions/define/system.d.ts +0 -7
- package/dist/core/functions/define/system.js +0 -12
- package/dist/core/functions/func-ref.d.ts +0 -16
- package/dist/core/functions/func-ref.js +0 -20
- package/dist/core/slot/deps.d.ts +0 -15
- package/dist/core/slot/deps.js +0 -88
- package/dist/core/slot/derived.d.ts +0 -32
- package/dist/core/slot/derived.js +0 -115
- package/dist/core/slot/platform-session-fields.d.ts +0 -38
- package/dist/core/slot/platform-session-fields.js +0 -48
- package/dist/core/slot/sf-schema.d.ts +0 -23
- package/dist/core/slot/sf-schema.js +0 -125
- package/dist/core/slot/sf-validate.d.ts +0 -14
- package/dist/core/slot/sf-validate.js +0 -132
- package/dist/core/slot/snapshot.d.ts +0 -18
- package/dist/core/slot/snapshot.js +0 -138
- package/dist/core/slot/write.d.ts +0 -48
- package/dist/core/slot/write.js +0 -426
- package/dist/core/stage/builders.d.ts +0 -54
- package/dist/core/stage/builders.js +0 -31
- package/dist/core/stage/condition.d.ts +0 -17
- package/dist/core/stage/condition.js +0 -198
- package/dist/core/stage/drive.d.ts +0 -43
- package/dist/core/stage/drive.js +0 -78
- package/dist/core/stage/finish-when.d.ts +0 -20
- package/dist/core/stage/finish-when.js +0 -82
- package/dist/core/stage/flatten.d.ts +0 -27
- package/dist/core/stage/flatten.js +0 -466
- package/dist/core/stage/index.d.ts +0 -15
- package/dist/core/stage/index.js +0 -13
- package/dist/core/stage/interrupts.d.ts +0 -28
- package/dist/core/stage/interrupts.js +0 -120
- package/dist/core/stage/ivr-fields.d.ts +0 -9
- package/dist/core/stage/ivr-fields.js +0 -75
- package/dist/core/stage/simulate.d.ts +0 -24
- package/dist/core/stage/simulate.js +0 -33
- package/dist/core/stage/transfer-fields.d.ts +0 -19
- package/dist/core/stage/transfer-fields.js +0 -129
- package/dist/core/stage/transfer-spec.d.ts +0 -48
- package/dist/core/stage/transfer-spec.js +0 -113
- package/dist/core/stage/types.d.ts +0 -208
- package/dist/core/stage/types.js +0 -2
- package/dist/core/stage/validate.d.ts +0 -11
- package/dist/core/stage/validate.js +0 -330
- package/dist/core/stage/walk.d.ts +0 -39
- package/dist/core/stage/walk.js +0 -119
- package/dist/fn-context/rpc/integration/list-bindings.d.ts +0 -21
- package/dist/fn-context/system-args.d.ts +0 -6
- package/dist/fn-context/system-args.js +0 -14
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
import { toHttpUrl } from './backend-url.js';
|
|
2
|
+
export function apiUrl(backendUrl, path) {
|
|
3
|
+
const url = toHttpUrl(backendUrl);
|
|
4
|
+
url.pathname = `/api/${path.replace(/^\//, '')}`;
|
|
5
|
+
url.search = '';
|
|
6
|
+
url.hash = '';
|
|
7
|
+
return url.toString();
|
|
8
|
+
}
|
|
9
|
+
export async function requestJson(url, init) {
|
|
10
|
+
const response = await fetch(url, { ...init, signal: AbortSignal.timeout(15_000) });
|
|
11
|
+
const payload = (await response.json().catch(() => ({})));
|
|
12
|
+
return { response, payload };
|
|
13
|
+
}
|
|
14
|
+
export async function postJson(url, body) {
|
|
15
|
+
return requestJson(url, {
|
|
16
|
+
method: 'POST',
|
|
17
|
+
headers: { 'Content-Type': 'application/json' },
|
|
18
|
+
body: JSON.stringify(body),
|
|
19
|
+
});
|
|
20
|
+
}
|
|
21
|
+
export async function getJson(url, token) {
|
|
22
|
+
return requestJson(url, {
|
|
23
|
+
method: 'GET',
|
|
24
|
+
headers: { Authorization: `Bearer ${token}` },
|
|
25
|
+
});
|
|
26
|
+
}
|
|
27
|
+
//# sourceMappingURL=http.js.map
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
export declare function runLogin(repoRoot: string, options: {
|
|
2
|
+
url?: string;
|
|
3
|
+
}): Promise<void>;
|
|
4
|
+
export declare function runLogout(repoRoot: string, options: {
|
|
5
|
+
url?: string;
|
|
6
|
+
}): Promise<void>;
|
|
7
|
+
export declare function runWhoami(repoRoot: string, options: {
|
|
8
|
+
url?: string;
|
|
9
|
+
}): Promise<void>;
|
|
10
|
+
//# sourceMappingURL=index.d.ts.map
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
import { authorizeServe, cacheServeAuthorization, readServeAuthorizationStatus, removeCachedServeAuthorization, resolveBackendUrl, } from './auth.js';
|
|
2
|
+
export async function runLogin(repoRoot, options) {
|
|
3
|
+
const backendUrl = resolveBackendUrl(repoRoot, options.url);
|
|
4
|
+
const authorization = await authorizeServe(backendUrl);
|
|
5
|
+
await cacheServeAuthorization(backendUrl, authorization);
|
|
6
|
+
console.log(`Logged in as ${authorization.user.email}`);
|
|
7
|
+
}
|
|
8
|
+
export async function runLogout(repoRoot, options) {
|
|
9
|
+
const backendUrl = resolveBackendUrl(repoRoot, options.url);
|
|
10
|
+
const removed = await removeCachedServeAuthorization(backendUrl);
|
|
11
|
+
console.log(removed ? `Logged out of ${backendUrl}` : `Not logged in to ${backendUrl}`);
|
|
12
|
+
}
|
|
13
|
+
export async function runWhoami(repoRoot, options) {
|
|
14
|
+
const backendUrl = resolveBackendUrl(repoRoot, options.url);
|
|
15
|
+
const status = await readServeAuthorizationStatus(backendUrl);
|
|
16
|
+
if (!status.loggedIn) {
|
|
17
|
+
console.log(`Not logged in to ${backendUrl}`);
|
|
18
|
+
return;
|
|
19
|
+
}
|
|
20
|
+
console.log(`Backend: ${backendUrl}`);
|
|
21
|
+
console.log(`User: ${status.authorization.user.email}`);
|
|
22
|
+
console.log(`Company: ${status.authorization.companyId}`);
|
|
23
|
+
console.log(status.expired
|
|
24
|
+
? `Session: expired at ${new Date(status.expiresAt).toISOString()} (run "login" again)`
|
|
25
|
+
: `Session: valid until ${new Date(status.expiresAt).toISOString()}`);
|
|
26
|
+
}
|
|
27
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
export type ProgressIndicator = {
|
|
2
|
+
start(message: string): void;
|
|
3
|
+
stop(message: string): void;
|
|
4
|
+
error(message: string): void;
|
|
5
|
+
};
|
|
6
|
+
/**
|
|
7
|
+
* A spinner on an interactive terminal; a single echoed start message otherwise. `stop`/`error`
|
|
8
|
+
* are spinner-only cosmetic detail — dropped silently when there's no spinner, since the CLI's
|
|
9
|
+
* top-level handler already prints failures for non-interactive runs.
|
|
10
|
+
*/
|
|
11
|
+
export declare function progressIndicator(): ProgressIndicator;
|
|
12
|
+
//# sourceMappingURL=progress.d.ts.map
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import { spinner } from '@clack/prompts';
|
|
2
|
+
/**
|
|
3
|
+
* A spinner on an interactive terminal; a single echoed start message otherwise. `stop`/`error`
|
|
4
|
+
* are spinner-only cosmetic detail — dropped silently when there's no spinner, since the CLI's
|
|
5
|
+
* top-level handler already prints failures for non-interactive runs.
|
|
6
|
+
*/
|
|
7
|
+
export function progressIndicator() {
|
|
8
|
+
const s = process.stdout.isTTY === true ? spinner() : null;
|
|
9
|
+
return {
|
|
10
|
+
start: (message) => (s ? s.start(message) : console.log(message)),
|
|
11
|
+
stop: (message) => s?.stop(message),
|
|
12
|
+
error: (message) => s?.error(message),
|
|
13
|
+
};
|
|
14
|
+
}
|
|
15
|
+
//# sourceMappingURL=progress.js.map
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
import { type ServeAuthorization } from './auth.js';
|
|
2
|
+
/** A fresh browser login, cached for the next run (a cache write failure only warns). */
|
|
3
|
+
export declare function loginServe(backendUrl: string, options?: {
|
|
4
|
+
openBrowser?: boolean;
|
|
5
|
+
}): Promise<ServeAuthorization>;
|
|
6
|
+
/** Drops a token the backend rejected from the cache, so the next run logs in again. */
|
|
7
|
+
export declare function forgetServeAuthorization(backendUrl: string, authorization: ServeAuthorization): Promise<void>;
|
|
8
|
+
//# sourceMappingURL=serve-login.d.ts.map
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
import { getLogger } from '../../logger.js';
|
|
2
|
+
import { authorizeServe, cacheServeAuthorization, clearCachedServeAuthorization, } from './auth.js';
|
|
3
|
+
const logger = getLogger(['runtime', 'auth']);
|
|
4
|
+
/** A fresh browser login, cached for the next run (a cache write failure only warns). */
|
|
5
|
+
export async function loginServe(backendUrl, options = {}) {
|
|
6
|
+
const authorization = await authorizeServe(backendUrl, options);
|
|
7
|
+
try {
|
|
8
|
+
await cacheServeAuthorization(backendUrl, authorization);
|
|
9
|
+
}
|
|
10
|
+
catch (error) {
|
|
11
|
+
logger.warn('could not cache serve authorization', error);
|
|
12
|
+
}
|
|
13
|
+
return authorization;
|
|
14
|
+
}
|
|
15
|
+
/** Drops a token the backend rejected from the cache, so the next run logs in again. */
|
|
16
|
+
export async function forgetServeAuthorization(backendUrl, authorization) {
|
|
17
|
+
try {
|
|
18
|
+
await clearCachedServeAuthorization(backendUrl, authorization.token);
|
|
19
|
+
}
|
|
20
|
+
catch (error) {
|
|
21
|
+
logger.warn('could not clear cached serve authorization', error);
|
|
22
|
+
}
|
|
23
|
+
}
|
|
24
|
+
//# sourceMappingURL=serve-login.js.map
|
|
@@ -0,0 +1,177 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* An integration test's calls in text (68 §4): a test customer calls one of the environment's simulated
|
|
3
|
+
* lines and talks in text, through the backend's `cli-auth/exec/call/*`. What the AI says comes back as
|
|
4
|
+
* text, what it does on the line (a keypad, a transfer, a voicemail) as an event the test answers. And the
|
|
5
|
+
* customer's side (68 §5.2): their texts and emails in, what the environment sent them, a call ringing them.
|
|
6
|
+
*/
|
|
7
|
+
import type { TurnSummary } from '../turns/index.js';
|
|
8
|
+
export type TextCallEvent = {
|
|
9
|
+
kind: 'keypad';
|
|
10
|
+
key: string;
|
|
11
|
+
say: string;
|
|
12
|
+
digits: string[];
|
|
13
|
+
} | {
|
|
14
|
+
kind: 'transfer';
|
|
15
|
+
to: string;
|
|
16
|
+
mode: string;
|
|
17
|
+
say: string;
|
|
18
|
+
language?: string;
|
|
19
|
+
} | {
|
|
20
|
+
kind: 'voicemail';
|
|
21
|
+
text: string;
|
|
22
|
+
} | {
|
|
23
|
+
kind: 'language';
|
|
24
|
+
language: string;
|
|
25
|
+
} | {
|
|
26
|
+
kind: 'listen';
|
|
27
|
+
ms: number;
|
|
28
|
+
} | {
|
|
29
|
+
kind: 'far_end';
|
|
30
|
+
farEnd: string;
|
|
31
|
+
decision: string;
|
|
32
|
+
};
|
|
33
|
+
export type TextCallEnd = {
|
|
34
|
+
/** `ai`: the AI ended it; `hangup`: the test did; `transfer`: a person took it; `no-answer` / `busy`: a call to them not taken. */
|
|
35
|
+
by: 'ai' | 'hangup' | 'transfer' | 'no-answer' | 'busy';
|
|
36
|
+
/** The Orchestrator, run once the call's `call.ended` was handled: its status and the follow-ups it booked. */
|
|
37
|
+
orchestrator: {
|
|
38
|
+
status: string;
|
|
39
|
+
failureCode?: string;
|
|
40
|
+
scheduled: unknown[];
|
|
41
|
+
cancelled: unknown[];
|
|
42
|
+
} | null;
|
|
43
|
+
};
|
|
44
|
+
/** What a step of the call came to. */
|
|
45
|
+
export type TextCallStep = {
|
|
46
|
+
callId: string;
|
|
47
|
+
/** What the AI said, in order. */
|
|
48
|
+
said: string[];
|
|
49
|
+
/** What it did on the line that a voice service would have done. */
|
|
50
|
+
events: TextCallEvent[];
|
|
51
|
+
/** The turns this step ran: what was heard, said, pressed and written. */
|
|
52
|
+
turns: TurnSummary[];
|
|
53
|
+
/** How the call ended, when it ended in this step. */
|
|
54
|
+
ended: TextCallEnd | null;
|
|
55
|
+
};
|
|
56
|
+
/** An open call in text. */
|
|
57
|
+
export type TextCall = {
|
|
58
|
+
callId: string;
|
|
59
|
+
/** The opening: what the AI said first. */
|
|
60
|
+
opening: TextCallStep;
|
|
61
|
+
/** The customer says `text`; the AI's turn on it. */
|
|
62
|
+
say(text: string): Promise<TextCallStep>;
|
|
63
|
+
/** The customer presses `value` for the keypad asked for (`key`: the last `keypad` event's when left out). */
|
|
64
|
+
press(value: string, key?: string): Promise<TextCallStep>;
|
|
65
|
+
/** The transfer asked for: `answered` (the person took it; the call ends for the AI) or `failed` (the AI goes on). */
|
|
66
|
+
transfer(outcome: 'answered' | 'failed'): Promise<TextCallStep>;
|
|
67
|
+
/** The customer hangs up: the wrap-up, `call.ended` and the Orchestrator run. */
|
|
68
|
+
hangup(): Promise<TextCallStep>;
|
|
69
|
+
/** Whether the call has ended. */
|
|
70
|
+
readonly ended: boolean;
|
|
71
|
+
};
|
|
72
|
+
type Post = <T>(what: string, body: unknown) => Promise<T>;
|
|
73
|
+
/** A test customer's number: North America's fictional range, the half simulated lines never take. */
|
|
74
|
+
export declare function testCustomerPhone(): string;
|
|
75
|
+
/** A test customer's email address: on the reserved `.test` domain, where simulated mailboxes write. */
|
|
76
|
+
export declare function testCustomerEmail(): string;
|
|
77
|
+
/** Opens a call from `from` to the simulated line `to`, and the object to go on with it. */
|
|
78
|
+
export declare function openTextCall(post: Post, input: {
|
|
79
|
+
from: string;
|
|
80
|
+
to: string;
|
|
81
|
+
}): Promise<TextCall>;
|
|
82
|
+
/** What a simulated line or mailbox sent a test customer, or a call ringing them. */
|
|
83
|
+
export type InboxItem = {
|
|
84
|
+
kind: 'sms';
|
|
85
|
+
id: string;
|
|
86
|
+
from: string;
|
|
87
|
+
line: string | null;
|
|
88
|
+
body: string;
|
|
89
|
+
at: string;
|
|
90
|
+
} | {
|
|
91
|
+
kind: 'email';
|
|
92
|
+
id: string;
|
|
93
|
+
from: string;
|
|
94
|
+
mailbox: string | null;
|
|
95
|
+
subject: string;
|
|
96
|
+
body: string;
|
|
97
|
+
threadId: string | null;
|
|
98
|
+
at: string;
|
|
99
|
+
} | {
|
|
100
|
+
kind: 'call';
|
|
101
|
+
id: string;
|
|
102
|
+
from: string;
|
|
103
|
+
line: string | null;
|
|
104
|
+
at: string;
|
|
105
|
+
status: 'ringing';
|
|
106
|
+
};
|
|
107
|
+
/** What a text or an email from a test customer came to: the run's turns, and what came back to them. */
|
|
108
|
+
export type SentResult = {
|
|
109
|
+
/** `debounce: 'real'`: stored and queued; the worker runs it once the texts stop coming, the reply comes to the inbox. */
|
|
110
|
+
queued?: boolean;
|
|
111
|
+
turns: TurnSummary[];
|
|
112
|
+
replies: InboxItem[];
|
|
113
|
+
orchestrator: TextCallEnd['orchestrator'];
|
|
114
|
+
};
|
|
115
|
+
/**
|
|
116
|
+
* A test customer (68 §5.2): a number and an address no carrier or provider has. They text and email the
|
|
117
|
+
* environment's simulated lines and mailboxes, find what it sent them in their inbox, and answer or let
|
|
118
|
+
* ring the calls it makes to them, as a person would, whenever the environment gets to it.
|
|
119
|
+
*/
|
|
120
|
+
export type TestCustomer = {
|
|
121
|
+
phone: string;
|
|
122
|
+
email: string;
|
|
123
|
+
/**
|
|
124
|
+
* They text the simulated line `to` (its name or number, `Main line` when left out); the run it started,
|
|
125
|
+
* now. `debounce: 'real'`: as a carrier's text, handled by the worker once their texts stop coming for
|
|
126
|
+
* 15 s (several in a row are one batch, one run): it returns at once (`queued`), the reply comes to
|
|
127
|
+
* `next('sms')`.
|
|
128
|
+
*/
|
|
129
|
+
text(body: string, options?: {
|
|
130
|
+
to?: string;
|
|
131
|
+
orchestrator?: 'now' | 'debounced';
|
|
132
|
+
debounce?: 'skip' | 'real';
|
|
133
|
+
}): Promise<SentResult>;
|
|
134
|
+
/** They email the simulated mailbox `to` (its name or address, `Support` when left out); `threadId` (an inbox email's) replies in its thread. */
|
|
135
|
+
sendEmail(input: {
|
|
136
|
+
body: string;
|
|
137
|
+
subject?: string;
|
|
138
|
+
to?: string;
|
|
139
|
+
threadId?: string | null;
|
|
140
|
+
orchestrator?: 'now' | 'debounced';
|
|
141
|
+
}): Promise<SentResult>;
|
|
142
|
+
/** What was sent to them since `since` (ISO; since this object was made when left out), oldest first, ringing calls included. */
|
|
143
|
+
inbox(since?: string): Promise<InboxItem[]>;
|
|
144
|
+
/**
|
|
145
|
+
* Waits for the next thing of `kind` sent to them that this object has not handed out yet (a scheduled text
|
|
146
|
+
* coming due, a call ringing): checked every 2 s, until `timeoutMs` (60 s when left out), then it fails.
|
|
147
|
+
*/
|
|
148
|
+
next<K extends InboxItem['kind']>(kind: K, options?: {
|
|
149
|
+
timeoutMs?: number;
|
|
150
|
+
match?: (item: Extract<InboxItem, {
|
|
151
|
+
kind: K;
|
|
152
|
+
}>) => boolean;
|
|
153
|
+
}): Promise<Extract<InboxItem, {
|
|
154
|
+
kind: K;
|
|
155
|
+
}>>;
|
|
156
|
+
/** They pick up a call ringing them: `as` a person (the AI opens), or their voicemail, a screener, a menu. */
|
|
157
|
+
answer(call: {
|
|
158
|
+
id: string;
|
|
159
|
+
} | string, options?: {
|
|
160
|
+
as?: 'human' | 'voicemail' | 'screening' | 'ivr';
|
|
161
|
+
said?: string;
|
|
162
|
+
orchestrator?: 'now' | 'debounced';
|
|
163
|
+
}): Promise<TextCall>;
|
|
164
|
+
/** They let it ring (`no-answer`) or are busy: the attempt ends as a carrier would end it, the Orchestrator runs. */
|
|
165
|
+
decline(call: {
|
|
166
|
+
id: string;
|
|
167
|
+
} | string, options?: {
|
|
168
|
+
status?: 'no-answer' | 'busy';
|
|
169
|
+
orchestrator?: 'now' | 'debounced';
|
|
170
|
+
}): Promise<TextCallStep>;
|
|
171
|
+
};
|
|
172
|
+
export declare function testCustomer(post: Post, input?: {
|
|
173
|
+
phone?: string;
|
|
174
|
+
email?: string;
|
|
175
|
+
}): TestCustomer;
|
|
176
|
+
export {};
|
|
177
|
+
//# sourceMappingURL=index.d.ts.map
|
|
@@ -0,0 +1,96 @@
|
|
|
1
|
+
/** A test customer's number: North America's fictional range, the half simulated lines never take. */
|
|
2
|
+
export function testCustomerPhone() {
|
|
3
|
+
// Area codes in use: a number of an unassigned one (889) is not taken as a phone number at all.
|
|
4
|
+
const codes = ['206', '212', '213', '303', '305', '312', '404', '415', '503', '512', '602', '617', '702', '713', '808', '917'];
|
|
5
|
+
const npa = codes[Math.floor(Math.random() * codes.length)];
|
|
6
|
+
const xx = 50 + Math.floor(Math.random() * 50);
|
|
7
|
+
return `+1${npa}55501${xx}`;
|
|
8
|
+
}
|
|
9
|
+
/** A test customer's email address: on the reserved `.test` domain, where simulated mailboxes write. */
|
|
10
|
+
export function testCustomerEmail() {
|
|
11
|
+
return `c-${Math.random().toString(36).slice(2, 10)}@customers.sim.test`;
|
|
12
|
+
}
|
|
13
|
+
/** Opens a call from `from` to the simulated line `to`, and the object to go on with it. */
|
|
14
|
+
export async function openTextCall(post, input) {
|
|
15
|
+
const opening = await post('call/open', input);
|
|
16
|
+
if (!opening.callId) {
|
|
17
|
+
throw new Error(`call: the line did not take it (no route, or resolve declined); the caller heard: ${opening.said.join(' ') || 'nothing'}`);
|
|
18
|
+
}
|
|
19
|
+
return textCall(post, opening);
|
|
20
|
+
}
|
|
21
|
+
function textCall(post, opening) {
|
|
22
|
+
let ended = opening.ended !== null;
|
|
23
|
+
let keypad = [...opening.events].reverse().find((e) => e.kind === 'keypad')?.key;
|
|
24
|
+
const step = async (what, body) => {
|
|
25
|
+
if (ended)
|
|
26
|
+
throw new Error(`call ${opening.callId}: it has ended`);
|
|
27
|
+
const result = await post(`call/${what}`, { callId: opening.callId, ...body });
|
|
28
|
+
ended = result.ended !== null;
|
|
29
|
+
keypad = [...result.events].reverse().find((e) => e.kind === 'keypad')?.key ?? keypad;
|
|
30
|
+
return result;
|
|
31
|
+
};
|
|
32
|
+
return {
|
|
33
|
+
callId: opening.callId,
|
|
34
|
+
opening,
|
|
35
|
+
say: (text) => step('say', { text }),
|
|
36
|
+
press: (value, key) => {
|
|
37
|
+
const asked = key ?? keypad;
|
|
38
|
+
if (!asked)
|
|
39
|
+
throw new Error(`call ${opening.callId}: no keypad was asked for; name its key`);
|
|
40
|
+
return step('press', { key: asked, value });
|
|
41
|
+
},
|
|
42
|
+
transfer: (outcome) => step('transfer', { outcome }),
|
|
43
|
+
hangup: () => step('hangup', {}),
|
|
44
|
+
get ended() {
|
|
45
|
+
return ended;
|
|
46
|
+
},
|
|
47
|
+
};
|
|
48
|
+
}
|
|
49
|
+
export function testCustomer(post, input = {}) {
|
|
50
|
+
const phone = input.phone ?? testCustomerPhone();
|
|
51
|
+
const email = input.email ?? testCustomerEmail();
|
|
52
|
+
const madeAt = new Date(Date.now() - 1_000).toISOString();
|
|
53
|
+
const handedOut = new Set();
|
|
54
|
+
const inbox = async (since) => (await post('sim/inbox', { phone, email, since: since ?? madeAt })).items;
|
|
55
|
+
const idOf = (call) => (typeof call === 'string' ? call : call.id);
|
|
56
|
+
const customer = {
|
|
57
|
+
phone,
|
|
58
|
+
email,
|
|
59
|
+
text: (body, options = {}) => post('sim/sms', {
|
|
60
|
+
from: phone,
|
|
61
|
+
to: options.to ?? 'Main line',
|
|
62
|
+
body,
|
|
63
|
+
...(options.orchestrator ? { orchestrator: options.orchestrator } : {}),
|
|
64
|
+
...(options.debounce ? { debounce: options.debounce } : {}),
|
|
65
|
+
}),
|
|
66
|
+
sendEmail: (mail) => post('sim/email', {
|
|
67
|
+
from: email,
|
|
68
|
+
to: mail.to ?? 'Support',
|
|
69
|
+
body: mail.body,
|
|
70
|
+
...(mail.subject !== undefined ? { subject: mail.subject } : {}),
|
|
71
|
+
...(mail.threadId ? { threadId: mail.threadId } : {}),
|
|
72
|
+
...(mail.orchestrator ? { orchestrator: mail.orchestrator } : {}),
|
|
73
|
+
}),
|
|
74
|
+
inbox,
|
|
75
|
+
async next(kind, options = {}) {
|
|
76
|
+
const deadline = Date.now() + (options.timeoutMs ?? 60_000);
|
|
77
|
+
for (;;) {
|
|
78
|
+
const found = (await inbox()).find((item) => item.kind === kind && !handedOut.has(item.id) && (!options.match || options.match(item)));
|
|
79
|
+
if (found) {
|
|
80
|
+
handedOut.add(found.id);
|
|
81
|
+
return found;
|
|
82
|
+
}
|
|
83
|
+
if (Date.now() >= deadline)
|
|
84
|
+
throw new Error(`${phone} / ${email}: no ${kind} came within ${options.timeoutMs ?? 60_000} ms`);
|
|
85
|
+
await new Promise((resolve) => setTimeout(resolve, 2_000));
|
|
86
|
+
}
|
|
87
|
+
},
|
|
88
|
+
async answer(call, options = {}) {
|
|
89
|
+
const opening = await post('call/answer', { callId: idOf(call), ...options });
|
|
90
|
+
return textCall(post, opening);
|
|
91
|
+
},
|
|
92
|
+
decline: (call, options = {}) => post('call/decline', { callId: idOf(call), ...options }),
|
|
93
|
+
};
|
|
94
|
+
return customer;
|
|
95
|
+
}
|
|
96
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -1,6 +1,9 @@
|
|
|
1
1
|
import path from 'node:path';
|
|
2
2
|
import { compileAll } from '../../builder/compile/compile-sequence.js';
|
|
3
|
+
import { typecheckRepo } from '../../builder/typecheck.js';
|
|
3
4
|
export async function runCompile(repoRoot, options = {}) {
|
|
5
|
+
// The same type check as a release, before any build is written: a type error blocks both.
|
|
6
|
+
await typecheckRepo(repoRoot, (text) => console.log(text));
|
|
4
7
|
const results = await compileAll(repoRoot, {
|
|
5
8
|
sequenceIds: options.sequenceId ? [options.sequenceId] : undefined,
|
|
6
9
|
});
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import { z } from 'zod';
|
|
2
|
+
export declare const COMPANY_REPO_CONFIG_PATH = ".superpanel/config.json";
|
|
3
|
+
declare const companyRepoConfigSchema: z.ZodObject<{
|
|
4
|
+
version: z.ZodLiteral<1>;
|
|
5
|
+
backendUrl: z.ZodString;
|
|
6
|
+
}, z.core.$strict>;
|
|
7
|
+
export type CompanyRepoConfig = z.infer<typeof companyRepoConfigSchema>;
|
|
8
|
+
export declare function readCompanyRepoConfig(repoRoot: string): CompanyRepoConfig | undefined;
|
|
9
|
+
export {};
|
|
10
|
+
//# sourceMappingURL=config.d.ts.map
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
import { readFileSync } from 'node:fs';
|
|
2
|
+
import path from 'node:path';
|
|
3
|
+
import { z } from 'zod';
|
|
4
|
+
import { toHttpUrl } from './auth/backend-url.js';
|
|
5
|
+
export const COMPANY_REPO_CONFIG_PATH = '.superpanel/config.json';
|
|
6
|
+
const companyRepoConfigSchema = z
|
|
7
|
+
.object({
|
|
8
|
+
version: z.literal(1),
|
|
9
|
+
backendUrl: z
|
|
10
|
+
.string()
|
|
11
|
+
.trim()
|
|
12
|
+
.min(1)
|
|
13
|
+
.refine((value) => {
|
|
14
|
+
try {
|
|
15
|
+
toHttpUrl(value);
|
|
16
|
+
return true;
|
|
17
|
+
}
|
|
18
|
+
catch {
|
|
19
|
+
return false;
|
|
20
|
+
}
|
|
21
|
+
}, 'must be a valid HTTP or HTTPS URL'),
|
|
22
|
+
})
|
|
23
|
+
.strict();
|
|
24
|
+
export function readCompanyRepoConfig(repoRoot) {
|
|
25
|
+
const file = path.join(repoRoot, COMPANY_REPO_CONFIG_PATH);
|
|
26
|
+
let raw;
|
|
27
|
+
try {
|
|
28
|
+
raw = readFileSync(file, 'utf8');
|
|
29
|
+
}
|
|
30
|
+
catch (error) {
|
|
31
|
+
const code = error && typeof error === 'object' && 'code' in error ? error.code : undefined;
|
|
32
|
+
if (code === 'ENOENT')
|
|
33
|
+
return undefined;
|
|
34
|
+
throw error;
|
|
35
|
+
}
|
|
36
|
+
let value;
|
|
37
|
+
try {
|
|
38
|
+
value = JSON.parse(raw);
|
|
39
|
+
}
|
|
40
|
+
catch {
|
|
41
|
+
throw new Error(`${COMPANY_REPO_CONFIG_PATH} is not valid JSON`);
|
|
42
|
+
}
|
|
43
|
+
const result = companyRepoConfigSchema.safeParse(value);
|
|
44
|
+
if (!result.success) {
|
|
45
|
+
const details = result.error.issues
|
|
46
|
+
.map((issue) => `${issue.path.join('.') || 'config'}: ${issue.message}`)
|
|
47
|
+
.join('; ');
|
|
48
|
+
throw new Error(`${COMPANY_REPO_CONFIG_PATH} has an invalid Superpanel configuration (${details})`);
|
|
49
|
+
}
|
|
50
|
+
return result.data;
|
|
51
|
+
}
|
|
52
|
+
//# sourceMappingURL=config.js.map
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
import type { ApiCall } from '../exec/trace.js';
|
|
2
|
+
import type { LogLine } from '../logs/index.js';
|
|
3
|
+
import { type TurnSummary } from '../turns/index.js';
|
|
4
|
+
/** The case ids in a request or an answer: every `instanceId`, and a started instance's `id`. */
|
|
5
|
+
export declare function casesIn(value: unknown, into: Set<string>, depth?: number): void;
|
|
6
|
+
export type DebugSources = {
|
|
7
|
+
/** A case's timeline and turns (`exec/debug`). */
|
|
8
|
+
caseDebug(instanceId: string): Promise<{
|
|
9
|
+
timeline: Array<{
|
|
10
|
+
at: string;
|
|
11
|
+
kind: string;
|
|
12
|
+
text: string;
|
|
13
|
+
[key: string]: unknown;
|
|
14
|
+
}>;
|
|
15
|
+
turns: TurnSummary[];
|
|
16
|
+
}>;
|
|
17
|
+
/** One turn, with what it began with (`readTurn`). */
|
|
18
|
+
readTurn(turnId: string): Promise<unknown>;
|
|
19
|
+
/** The repository's log lines on a case since a time. */
|
|
20
|
+
logs(instanceId: string, since: string): Promise<LogLine[]>;
|
|
21
|
+
};
|
|
22
|
+
export type DebugTest = {
|
|
23
|
+
name: string;
|
|
24
|
+
error: unknown;
|
|
25
|
+
startedAt: string;
|
|
26
|
+
cases: string[];
|
|
27
|
+
calls: ApiCall[];
|
|
28
|
+
};
|
|
29
|
+
/** Writes the package; its directory. Never throws: a package that could not be read says so in its summary. */
|
|
30
|
+
export declare function writeDebugPackage(root: string, test: DebugTest, sources: DebugSources): Promise<string>;
|
|
31
|
+
//# sourceMappingURL=index.d.ts.map
|
|
@@ -0,0 +1,119 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* A test's debug package (68 §9): when an integration test fails (or every test, with `debug: 'always'`),
|
|
3
|
+
* everything that can be seen of the cases it touched is written next to the repository, under
|
|
4
|
+
* `.superpanel/tests/<run>/<test>/`, for a person or an agent to read with `grep` / `jq`:
|
|
5
|
+
*
|
|
6
|
+
* summary.md read this first: the failure, the cases, their turns, the commands to go on with
|
|
7
|
+
* timeline.jsonl the cases' timelines and turns, by time
|
|
8
|
+
* turns/<n>.json each turn: what was heard, said, pressed and written, and what it began with
|
|
9
|
+
* logs.jsonl what this repository's code logged on those cases
|
|
10
|
+
* calls.jsonl the `ctx.api` calls the test made
|
|
11
|
+
*
|
|
12
|
+
* The cases are the ones the test's requests named or were answered with (`instanceId`, a started
|
|
13
|
+
* instance): a test that makes its own contacts and cases (TESTING.md) finds all of its own.
|
|
14
|
+
*/
|
|
15
|
+
import { mkdirSync, writeFileSync } from 'node:fs';
|
|
16
|
+
import path from 'node:path';
|
|
17
|
+
import { formatTurnLine } from '../turns/index.js';
|
|
18
|
+
const UUID = /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i;
|
|
19
|
+
/** The case ids in a request or an answer: every `instanceId`, and a started instance's `id`. */
|
|
20
|
+
export function casesIn(value, into, depth = 0) {
|
|
21
|
+
if (!value || typeof value !== 'object' || depth > 6)
|
|
22
|
+
return;
|
|
23
|
+
if (Array.isArray(value)) {
|
|
24
|
+
for (const item of value.slice(0, 200))
|
|
25
|
+
casesIn(item, into, depth + 1);
|
|
26
|
+
return;
|
|
27
|
+
}
|
|
28
|
+
for (const [key, item] of Object.entries(value)) {
|
|
29
|
+
if ((key === 'instanceId' || key === 'currentInstanceId') && typeof item === 'string' && UUID.test(item))
|
|
30
|
+
into.add(item);
|
|
31
|
+
else if (key === 'instance' && item && typeof item === 'object' && typeof item.id === 'string' && UUID.test(item.id))
|
|
32
|
+
into.add(item.id);
|
|
33
|
+
else
|
|
34
|
+
casesIn(item, into, depth + 1);
|
|
35
|
+
}
|
|
36
|
+
}
|
|
37
|
+
const slug = (text) => text.toLowerCase().replace(/[^a-z0-9]+/g, '-').replace(/^-|-$/g, '').slice(0, 80) || 'test';
|
|
38
|
+
function messageOf(error) {
|
|
39
|
+
if (!error)
|
|
40
|
+
return '';
|
|
41
|
+
if (error instanceof Error)
|
|
42
|
+
return error.message;
|
|
43
|
+
return String(error);
|
|
44
|
+
}
|
|
45
|
+
/** Writes the package; its directory. Never throws: a package that could not be read says so in its summary. */
|
|
46
|
+
export async function writeDebugPackage(root, test, sources) {
|
|
47
|
+
const dir = path.join(root, slug(test.name));
|
|
48
|
+
mkdirSync(path.join(dir, 'turns'), { recursive: true });
|
|
49
|
+
const problems = [];
|
|
50
|
+
const timeline = [];
|
|
51
|
+
const turns = [];
|
|
52
|
+
const logs = [];
|
|
53
|
+
for (const instanceId of test.cases) {
|
|
54
|
+
try {
|
|
55
|
+
const debug = await sources.caseDebug(instanceId);
|
|
56
|
+
for (const entry of debug.timeline)
|
|
57
|
+
timeline.push({ ...entry, source: 'timeline', instanceId });
|
|
58
|
+
for (const turn of debug.turns) {
|
|
59
|
+
turns.push(turn);
|
|
60
|
+
timeline.push({ at: turn.startedAt, source: 'turn', instanceId, kind: `turn:${turn.kind}`, text: formatTurnLine(turn) });
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
catch (error) {
|
|
64
|
+
problems.push(`case ${instanceId}: ${messageOf(error)}`);
|
|
65
|
+
}
|
|
66
|
+
try {
|
|
67
|
+
logs.push(...(await sources.logs(instanceId, test.startedAt)));
|
|
68
|
+
}
|
|
69
|
+
catch (error) {
|
|
70
|
+
problems.push(`logs of case ${instanceId}: ${messageOf(error)}`);
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
timeline.sort((a, b) => a.at.localeCompare(b.at));
|
|
74
|
+
for (const line of logs)
|
|
75
|
+
timeline.push({ at: line.at, source: 'log', instanceId: '', kind: `log:${line.level}`, text: line.message ?? '' });
|
|
76
|
+
timeline.sort((a, b) => a.at.localeCompare(b.at));
|
|
77
|
+
const ofTest = turns.filter((t) => t.startedAt >= test.startedAt);
|
|
78
|
+
for (const turn of ofTest.slice(-30)) {
|
|
79
|
+
try {
|
|
80
|
+
writeFileSync(path.join(dir, 'turns', `${String(turn.seq).padStart(3, '0')}-${turn.kind}-${turn.id.slice(0, 8)}.json`), `${JSON.stringify(await sources.readTurn(turn.id), null, 2)}\n`);
|
|
81
|
+
}
|
|
82
|
+
catch (error) {
|
|
83
|
+
problems.push(`turn ${turn.id}: ${messageOf(error)}`);
|
|
84
|
+
}
|
|
85
|
+
}
|
|
86
|
+
const jsonl = (rows) => rows.map((row) => JSON.stringify(row)).join('\n') + (rows.length ? '\n' : '');
|
|
87
|
+
writeFileSync(path.join(dir, 'timeline.jsonl'), jsonl(timeline));
|
|
88
|
+
writeFileSync(path.join(dir, 'logs.jsonl'), jsonl(logs));
|
|
89
|
+
writeFileSync(path.join(dir, 'calls.jsonl'), jsonl(test.calls));
|
|
90
|
+
const last = ofTest.at(-1) ?? turns.at(-1);
|
|
91
|
+
const summary = [
|
|
92
|
+
`# ${test.name}`,
|
|
93
|
+
'',
|
|
94
|
+
test.error ? `**Failed:** ${messageOf(test.error).trim()}` : '**Passed.**',
|
|
95
|
+
'',
|
|
96
|
+
`Started ${test.startedAt}. Cases: ${test.cases.length ? test.cases.map((id) => `\`${id}\``).join(', ') : 'none named by the test'}.`,
|
|
97
|
+
'',
|
|
98
|
+
'## Turns in this test',
|
|
99
|
+
'',
|
|
100
|
+
...(ofTest.length ? ofTest.map((t) => `- ${formatTurnLine(t)}`) : ['- none']),
|
|
101
|
+
'',
|
|
102
|
+
'## Go on with',
|
|
103
|
+
'',
|
|
104
|
+
...(last
|
|
105
|
+
? [
|
|
106
|
+
`- the last turn, as it ran: \`npm run turns -- export ${last.id}\` (then \`renderPage(fromTurn(dir))\`)`,
|
|
107
|
+
`- again, on your code as it is now, 5 times, nothing kept: \`npm run turn -- --from ${last.id} --repeat 5\``,
|
|
108
|
+
]
|
|
109
|
+
: []),
|
|
110
|
+
...test.cases.map((id) => `- what the code logged on case ${id}: \`npm run logs -- --instance ${id} --since 2h\``),
|
|
111
|
+
'',
|
|
112
|
+
'Files: `timeline.jsonl` (by time: the cases\' timelines, turns, log lines), `turns/` (each turn whole), `logs.jsonl`, `calls.jsonl` (the test\'s `ctx.api` calls).',
|
|
113
|
+
...(problems.length ? ['', '## Not read', '', ...problems.map((p) => `- ${p}`)] : []),
|
|
114
|
+
'',
|
|
115
|
+
].join('\n');
|
|
116
|
+
writeFileSync(path.join(dir, 'summary.md'), summary);
|
|
117
|
+
return dir;
|
|
118
|
+
}
|
|
119
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
import { traceApi, type ApiCall } from './trace.js';
|
|
2
|
+
export { traceApi, type ApiCall };
|
|
3
|
+
export type ExecOptions = {
|
|
4
|
+
env?: string;
|
|
5
|
+
instance?: string;
|
|
6
|
+
args?: string;
|
|
7
|
+
url?: string;
|
|
8
|
+
/** Print only the result's JSON (for a script or an agent). */
|
|
9
|
+
json?: boolean;
|
|
10
|
+
};
|
|
11
|
+
/** The run's outcome, as `result.json` keeps it and `--json` prints it. */
|
|
12
|
+
export type ExecResult = {
|
|
13
|
+
ok: boolean;
|
|
14
|
+
file: string;
|
|
15
|
+
environment: {
|
|
16
|
+
id: string;
|
|
17
|
+
name: string;
|
|
18
|
+
};
|
|
19
|
+
instance: {
|
|
20
|
+
id: string;
|
|
21
|
+
sequence: string;
|
|
22
|
+
} | null;
|
|
23
|
+
startedAt: string;
|
|
24
|
+
durationMs: number;
|
|
25
|
+
/** `ctx.api` calls made, and how many of them failed. */
|
|
26
|
+
calls: {
|
|
27
|
+
total: number;
|
|
28
|
+
failed: number;
|
|
29
|
+
};
|
|
30
|
+
value?: unknown;
|
|
31
|
+
error?: {
|
|
32
|
+
message: string;
|
|
33
|
+
stack?: string;
|
|
34
|
+
};
|
|
35
|
+
/** Where the run's files are. */
|
|
36
|
+
dir: string;
|
|
37
|
+
};
|
|
38
|
+
export declare function runExec(repoRoot: string, file: string, options?: ExecOptions): Promise<ExecResult>;
|
|
39
|
+
//# sourceMappingURL=index.d.ts.map
|