@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,102 @@
|
|
|
1
|
+
import { spawn } from 'node:child_process';
|
|
2
|
+
import { existsSync, readFileSync, realpathSync } from 'node:fs';
|
|
3
|
+
import { createRequire } from 'node:module';
|
|
4
|
+
import path from 'node:path';
|
|
5
|
+
import { generateAll } from './codegen/generate.js';
|
|
6
|
+
import { RUNTIME_PACKAGE, isCompanyApiFile } from './company-imports.js';
|
|
7
|
+
/**
|
|
8
|
+
* The TypeScript this runtime pins: the check never depends on whether, or which, `typescript` the repo
|
|
9
|
+
* lists. The types it reads (`@types/node`) are the repo's own devDependencies, which a release installs too.
|
|
10
|
+
*/
|
|
11
|
+
function pinnedTypescript() {
|
|
12
|
+
const require = createRequire(import.meta.url);
|
|
13
|
+
const pkgPath = require.resolve('typescript/package.json');
|
|
14
|
+
const pkg = JSON.parse(readFileSync(pkgPath, 'utf8'));
|
|
15
|
+
const bin = typeof pkg.bin === 'string' ? pkg.bin : pkg.bin.tsc;
|
|
16
|
+
return { version: pkg.version, tsc: path.join(path.dirname(pkgPath), bin) };
|
|
17
|
+
}
|
|
18
|
+
function runTsc(tsc, args, cwd) {
|
|
19
|
+
return new Promise((resolve, reject) => {
|
|
20
|
+
const child = spawn(process.execPath, [tsc, ...args], { cwd, stdio: ['ignore', 'pipe', 'pipe'] });
|
|
21
|
+
let output = '';
|
|
22
|
+
child.stdout.setEncoding('utf8');
|
|
23
|
+
child.stderr.setEncoding('utf8');
|
|
24
|
+
child.stdout.on('data', (chunk) => {
|
|
25
|
+
output += chunk;
|
|
26
|
+
});
|
|
27
|
+
child.stderr.on('data', (chunk) => {
|
|
28
|
+
output += chunk;
|
|
29
|
+
});
|
|
30
|
+
child.once('error', reject);
|
|
31
|
+
child.once('close', (status) => resolve({ status, output }));
|
|
32
|
+
});
|
|
33
|
+
}
|
|
34
|
+
/** Whether a `paths` key (`@scope/*`, `*`, an exact name) can match an import of `@superpanel-sdk/…`. */
|
|
35
|
+
function mapsSdkImports(key) {
|
|
36
|
+
const star = key.indexOf('*');
|
|
37
|
+
if (star === -1)
|
|
38
|
+
return key.startsWith('@superpanel-sdk/');
|
|
39
|
+
const prefix = key.slice(0, star);
|
|
40
|
+
return '@superpanel-sdk/'.startsWith(prefix) || prefix.startsWith('@superpanel-sdk/');
|
|
41
|
+
}
|
|
42
|
+
/**
|
|
43
|
+
* Why the repository's tsconfig would let its types see more than the company API (09-runtime-compat §6):
|
|
44
|
+
* `customConditions` resolve the platform's entries, and a `paths` entry can point a runtime import at
|
|
45
|
+
* any file. The one mapping it may have is `@superpanel-sdk/runtime` to a runtime's company API (the
|
|
46
|
+
* runtime's own fixtures map it to its source). Null when the config does neither.
|
|
47
|
+
*/
|
|
48
|
+
async function tsconfigImportIssue(tsc, root) {
|
|
49
|
+
// The effective config, `extends` applied. A config tsc cannot read fails the type check itself.
|
|
50
|
+
const { status, output } = await runTsc(tsc, ['-p', path.join(root, 'tsconfig.json'), '--showConfig'], root);
|
|
51
|
+
if (status !== 0)
|
|
52
|
+
return null;
|
|
53
|
+
let options;
|
|
54
|
+
try {
|
|
55
|
+
options = JSON.parse(output).compilerOptions ?? {};
|
|
56
|
+
}
|
|
57
|
+
catch {
|
|
58
|
+
return null;
|
|
59
|
+
}
|
|
60
|
+
if (options.customConditions && options.customConditions.length > 0) {
|
|
61
|
+
return `customConditions (${options.customConditions.join(', ')}) can change how ${RUNTIME_PACKAGE} resolves; company code gets it as it resolves by default`;
|
|
62
|
+
}
|
|
63
|
+
const base = options.baseUrl ? path.resolve(root, options.baseUrl) : root;
|
|
64
|
+
for (const [key, targets] of Object.entries(options.paths ?? {})) {
|
|
65
|
+
if (!mapsSdkImports(key))
|
|
66
|
+
continue;
|
|
67
|
+
if (key === RUNTIME_PACKAGE && targets.every((target) => isCompanyApiFile(path.resolve(base, target))))
|
|
68
|
+
continue;
|
|
69
|
+
return `paths maps '${key}': company code imports ${RUNTIME_PACKAGE} as the package resolves it`;
|
|
70
|
+
}
|
|
71
|
+
return null;
|
|
72
|
+
}
|
|
73
|
+
/**
|
|
74
|
+
* The repository's type check, as `npm run typecheck` runs it locally: `type:gen`, then `tsc --noEmit`
|
|
75
|
+
* with the repo's own `tsconfig.json`. With no sequence it writes only the root `__gen` (the local
|
|
76
|
+
* `type:gen` needs a sequence). The compile runs it itself, so a type error blocks a release in every
|
|
77
|
+
* environment; the platform never calls the repo's `typecheck` script.
|
|
78
|
+
*/
|
|
79
|
+
export async function typecheckRepo(repoRoot, onLog) {
|
|
80
|
+
const tsconfig = path.join(repoRoot, 'tsconfig.json');
|
|
81
|
+
if (!existsSync(tsconfig)) {
|
|
82
|
+
throw new Error('tsconfig.json is required: the compile type-checks the repository with it');
|
|
83
|
+
}
|
|
84
|
+
// type:gen first. A repository with no sequence still releases its company code (22 §11): only the
|
|
85
|
+
// root __gen is written then, which its root code's `ctx.api.db` is typed by.
|
|
86
|
+
await generateAll(repoRoot);
|
|
87
|
+
const { version, tsc } = pinnedTypescript();
|
|
88
|
+
onLog?.(`Type checking with TypeScript ${version}...`);
|
|
89
|
+
// tsc names files relative to its working directory, which is the real path (macOS's /var is a link):
|
|
90
|
+
// run it there, so the output names `sequences/…` as the repository has them.
|
|
91
|
+
const root = realpathSync(repoRoot);
|
|
92
|
+
const configIssue = await tsconfigImportIssue(tsc, root);
|
|
93
|
+
if (configIssue)
|
|
94
|
+
throw new Error(`Type check failed: tsconfig.json ${configIssue} (AGENTS.md, "Imports")`);
|
|
95
|
+
// Plain output whatever the terminal: the compile log is text.
|
|
96
|
+
const { status, output } = await runTsc(tsc, ['-p', path.join(root, 'tsconfig.json'), '--noEmit', '--pretty', 'false'], root);
|
|
97
|
+
if (status !== 0) {
|
|
98
|
+
throw new Error(`Type check failed (tsc --noEmit):\n${output.trim()}`);
|
|
99
|
+
}
|
|
100
|
+
onLog?.('Type check passed');
|
|
101
|
+
}
|
|
102
|
+
//# sourceMappingURL=typecheck.js.map
|
package/dist/builder/types.d.ts
CHANGED
|
@@ -1,36 +1,144 @@
|
|
|
1
|
-
import type {
|
|
2
|
-
import type {
|
|
1
|
+
import type { CaseOrchestratorManifest } from '../core/case-orchestrator/types.js';
|
|
2
|
+
import type { ScheduleManifest } from '../core/functions/schedules.js';
|
|
3
|
+
import type { FunctionRegistry } from '../core/functions/types.js';
|
|
4
|
+
import type { OfficeHours } from '../core/office-hours/index.js';
|
|
5
|
+
import type { ActionParameters } from '../core/page/define.js';
|
|
6
|
+
import type { CompiledExperiments, CompiledVariables } from '../core/sequence/index.js';
|
|
3
7
|
import type { CompiledSlotSchema } from '../core/slot/types.js';
|
|
4
|
-
import type { CompiledAgenda } from '../core/stage/types.js';
|
|
5
8
|
export type RuntimePackageSource = 'local-file' | 'npm';
|
|
6
9
|
/**
|
|
7
|
-
*
|
|
8
|
-
* Omitted for now:
|
|
10
|
+
* Deterministic compiled sequence artifact (22 §4.1; grows over time).
|
|
11
|
+
* Omitted for now: caseManual.
|
|
9
12
|
*
|
|
10
13
|
* Published builds include per-fn `code`. Local serve overlay uses the same
|
|
11
14
|
* type but omits `code` — functions run on the laptop via WebSocket.
|
|
15
|
+
* Compilation time belongs to the database row's `createdAt`, never this artifact.
|
|
16
|
+
*
|
|
17
|
+
* Contract: the company repo's own runtime writes this, and the platform engine (always the
|
|
18
|
+
* newest runtime) reads it, for as long as that runtime version is supported.
|
|
19
|
+
*
|
|
20
|
+
* From launch on (docs/impl/09-runtime-compat.md), grow it only: never rename or remove a field,
|
|
21
|
+
* and give every new field a default the engine applies when an older build lacks it. A breaking
|
|
22
|
+
* change needs a new major runtime plus an announced end-of-support date for the old one. Before
|
|
23
|
+
* launch there are no older builds to read, so fields are added as required, with no defaults.
|
|
12
24
|
*/
|
|
13
25
|
export type SequenceBuild = {
|
|
26
|
+
/** The directory name under `sequences/`: the sequence's identity (22 §10.2). */
|
|
14
27
|
sequenceId: string;
|
|
15
|
-
|
|
28
|
+
/** What people see: `defineSequence({ meta: { label } })`, else the directory name. Synced on release. */
|
|
29
|
+
label: string;
|
|
30
|
+
/** Version of the runtime whose compiler produced this build (the company repo's runtime). */
|
|
16
31
|
engineVersion: string;
|
|
17
32
|
/** Installed @superpanel-sdk/runtime version resolved from the company repo. */
|
|
18
|
-
runtimeVersion
|
|
19
|
-
/** Dependency source declared by the company repo.
|
|
20
|
-
runtimeSource
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
/**
|
|
24
|
-
|
|
25
|
-
|
|
33
|
+
runtimeVersion: string;
|
|
34
|
+
/** Dependency source declared by the company repo. */
|
|
35
|
+
runtimeSource: RuntimePackageSource;
|
|
36
|
+
/** The company table each run is about (`defineSequence({ subject })`, 14-sequence-instance §4.5). */
|
|
37
|
+
subject: string;
|
|
38
|
+
/** Run variables from `variables.ts` (14 §4.4); `{}` without the file. */
|
|
39
|
+
variables: CompiledVariables;
|
|
40
|
+
/** Experiments from `variables.ts`: how runs that do not set a variable are split. */
|
|
41
|
+
experiments: CompiledExperiments;
|
|
26
42
|
/** Per-function registry. `code` omitted on local overlay. */
|
|
27
43
|
functions: FunctionRegistry;
|
|
44
|
+
/** Platform event handlers keyed by event name; kind fixed by the event table. */
|
|
45
|
+
events: FunctionRegistry;
|
|
46
|
+
/**
|
|
47
|
+
* Sequence office hours from `sequences/<id>/office-hours.ts`. `null` when the sequence has no
|
|
48
|
+
* file (always open).
|
|
49
|
+
*/
|
|
50
|
+
officeHours: OfficeHours | null;
|
|
51
|
+
/**
|
|
52
|
+
* The Case Orchestrator entry point (`orchestrator` in `functions`). `null` / absent when the
|
|
53
|
+
* sequence has no orchestrator.ts (older builds lack the field).
|
|
54
|
+
*/
|
|
55
|
+
orchestrator?: CaseOrchestratorManifest | null;
|
|
56
|
+
/**
|
|
57
|
+
* The conversation's page script (`sequences/<id>/context.page.ts`, 15 §10.2), bundled: evaluates to
|
|
58
|
+
* the entry that renders the page or runs one of its actions. Always carries its code, the serve
|
|
59
|
+
* overlay included: the platform runs it next to the database. Every sequence has one. `channels`:
|
|
60
|
+
* the channels it has a page function for (17b §9.1).
|
|
61
|
+
*/
|
|
62
|
+
contextPage: {
|
|
63
|
+
code: string;
|
|
64
|
+
channels: string[];
|
|
65
|
+
/** The functions' parameters, from their `args` types (38 §2.4): what a function button has filled. */
|
|
66
|
+
functions?: Record<string, {
|
|
67
|
+
parameters: ActionParameters;
|
|
68
|
+
}>;
|
|
69
|
+
/** The page's actions' parameters, from their `args` types (38 §2.9): what a page action button has filled. */
|
|
70
|
+
actions?: Record<string, {
|
|
71
|
+
parameters: ActionParameters;
|
|
72
|
+
}>;
|
|
73
|
+
};
|
|
74
|
+
};
|
|
75
|
+
/**
|
|
76
|
+
* The company build (22-sequence-as-business-unit §4.1): one release compiles the whole repository
|
|
77
|
+
* into it, and one Cloudflare Worker serves all of it. Same contract as {@link SequenceBuild}.
|
|
78
|
+
*/
|
|
79
|
+
export type CompanyBuild = {
|
|
80
|
+
/** The company's own code, outside any sequence (§5). */
|
|
81
|
+
company: CompanyPart;
|
|
82
|
+
/** Every sequence's build, keyed by its directory name under `sequences/`. */
|
|
83
|
+
sequences: Record<string, SequenceBuild>;
|
|
84
|
+
};
|
|
85
|
+
/**
|
|
86
|
+
* Company-level functions (`resolve.ts` and `webhook.ts` at the repository root): they run with no
|
|
87
|
+
* sequence. In the company Worker they are named `@company/<name>`; `@` keeps them apart from any
|
|
88
|
+
* sequence's directory.
|
|
89
|
+
*/
|
|
90
|
+
export type CompanyPart = {
|
|
91
|
+
/**
|
|
92
|
+
* `companyResolve`, `webhook.received` when the repository has `webhook.ts`, and `schedule.<name>`
|
|
93
|
+
* for each job of `schedules.ts`. `code` omitted on the local serve overlay.
|
|
94
|
+
*/
|
|
95
|
+
functions: FunctionRegistry;
|
|
96
|
+
/** The company's recurring jobs (root `schedules.ts`, 25 §6.4), by name. Absent: none. */
|
|
97
|
+
schedules?: ScheduleManifest;
|
|
98
|
+
/**
|
|
99
|
+
* The company schema: what every function, page and the Orchestrator read and write, in every
|
|
100
|
+
* sequence (28 §2: a sequence has no schema of its own, only its `subject`).
|
|
101
|
+
*/
|
|
102
|
+
schema: CompiledSlotSchema;
|
|
28
103
|
/**
|
|
29
|
-
*
|
|
30
|
-
*
|
|
104
|
+
* The runtime the repository installs, which the company Worker runs (every sequence compiles
|
|
105
|
+
* against the same one): known with no sequence at all (22 §11).
|
|
31
106
|
*/
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
agendas: Record<string, CompiledAgenda>;
|
|
107
|
+
runtimeVersion: string;
|
|
108
|
+
runtimeSource: RuntimePackageSource;
|
|
35
109
|
};
|
|
110
|
+
/** Where company-level functions live, in place of a sequence's directory name. */
|
|
111
|
+
export declare const COMPANY_SCOPE = "@company";
|
|
112
|
+
/** The page script's name among its sequence's functions (its `functionKey`): reserved, no function takes it. */
|
|
113
|
+
export declare const CONTEXT_PAGE_FUNCTION = "contextPage";
|
|
114
|
+
/**
|
|
115
|
+
* The name the platform knows a piece of compiled company code by, wherever it runs: its scope (the
|
|
116
|
+
* sequence's directory name, or {@link COMPANY_SCOPE}), then its name. A Worker function is invoked
|
|
117
|
+
* by it (the company Worker's registry is keyed by it); the page script, run in the page sandbox, is
|
|
118
|
+
* `<sequence>/contextPage`. Directory names never contain `/` or `@`, so names of different scopes
|
|
119
|
+
* cannot meet.
|
|
120
|
+
*/
|
|
121
|
+
export declare function functionKey(scope: string, name: string): string;
|
|
122
|
+
/**
|
|
123
|
+
* The file a piece of code's stack frames name, by its {@link functionKey}: its `//# sourceURL` in the
|
|
124
|
+
* company Worker, the file name the page sandbox runs it as. Its source map is found by that key.
|
|
125
|
+
*/
|
|
126
|
+
export declare function functionScriptFile(key: string): string;
|
|
127
|
+
/**
|
|
128
|
+
* The source map of one compiled function or page script (v3, no `sourcesContent`): where a position
|
|
129
|
+
* in its `code` was in the company's own files, by path from the repository root. Every other source
|
|
130
|
+
* (the compile's entry, the runtime, dependencies) is `null`, the platform's code. Kept out of the
|
|
131
|
+
* build, which is read on every run: a failure's stack is turned into the company's with it.
|
|
132
|
+
*/
|
|
133
|
+
export type FunctionSourceMap = {
|
|
134
|
+
version: 3;
|
|
135
|
+
sources: Array<string | null>;
|
|
136
|
+
names: string[];
|
|
137
|
+
mappings: string;
|
|
138
|
+
};
|
|
139
|
+
/**
|
|
140
|
+
* Source maps of compiled code by its {@link functionKey}: a sequence's functions, events and page
|
|
141
|
+
* script (`<sequence>/contextPage`), the company's functions (`@company/<name>`).
|
|
142
|
+
*/
|
|
143
|
+
export type SourceMaps = Record<string, FunctionSourceMap>;
|
|
36
144
|
//# sourceMappingURL=types.d.ts.map
|
package/dist/builder/types.js
CHANGED
|
@@ -1,2 +1,22 @@
|
|
|
1
|
-
|
|
1
|
+
/** Where company-level functions live, in place of a sequence's directory name. */
|
|
2
|
+
export const COMPANY_SCOPE = '@company';
|
|
3
|
+
/** The page script's name among its sequence's functions (its `functionKey`): reserved, no function takes it. */
|
|
4
|
+
export const CONTEXT_PAGE_FUNCTION = 'contextPage';
|
|
5
|
+
/**
|
|
6
|
+
* The name the platform knows a piece of compiled company code by, wherever it runs: its scope (the
|
|
7
|
+
* sequence's directory name, or {@link COMPANY_SCOPE}), then its name. A Worker function is invoked
|
|
8
|
+
* by it (the company Worker's registry is keyed by it); the page script, run in the page sandbox, is
|
|
9
|
+
* `<sequence>/contextPage`. Directory names never contain `/` or `@`, so names of different scopes
|
|
10
|
+
* cannot meet.
|
|
11
|
+
*/
|
|
12
|
+
export function functionKey(scope, name) {
|
|
13
|
+
return `${scope}/${name}`;
|
|
14
|
+
}
|
|
15
|
+
/**
|
|
16
|
+
* The file a piece of code's stack frames name, by its {@link functionKey}: its `//# sourceURL` in the
|
|
17
|
+
* company Worker, the file name the page sandbox runs it as. Its source map is found by that key.
|
|
18
|
+
*/
|
|
19
|
+
export function functionScriptFile(key) {
|
|
20
|
+
return `fn/${key}.js`;
|
|
21
|
+
}
|
|
2
22
|
//# sourceMappingURL=types.js.map
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
export type ServeAuthorization = {
|
|
2
|
+
token: string;
|
|
3
|
+
user: {
|
|
4
|
+
id: string;
|
|
5
|
+
email: string;
|
|
6
|
+
};
|
|
7
|
+
companyId: string;
|
|
8
|
+
companyName: string | null;
|
|
9
|
+
};
|
|
10
|
+
export declare function resolveBackendUrl(repoRoot: string, optionUrl?: string): string;
|
|
11
|
+
export declare function loadCachedServeAuthorization(backendUrl: string, file?: string): Promise<ServeAuthorization | null>;
|
|
12
|
+
export declare function cacheServeAuthorization(backendUrl: string, authorization: ServeAuthorization, file?: string): Promise<void>;
|
|
13
|
+
export declare function clearCachedServeAuthorization(backendUrl: string, token: string, file?: string): Promise<void>;
|
|
14
|
+
export declare function removeCachedServeAuthorization(backendUrl: string, file?: string): Promise<boolean>;
|
|
15
|
+
export type ServeAuthorizationStatus = {
|
|
16
|
+
loggedIn: false;
|
|
17
|
+
} | {
|
|
18
|
+
loggedIn: true;
|
|
19
|
+
authorization: ServeAuthorization;
|
|
20
|
+
expiresAt: number;
|
|
21
|
+
expired: boolean;
|
|
22
|
+
};
|
|
23
|
+
export declare function readServeAuthorizationStatus(backendUrl: string, file?: string): Promise<ServeAuthorizationStatus>;
|
|
24
|
+
export type ServeEnvironment = {
|
|
25
|
+
id: string;
|
|
26
|
+
name: string;
|
|
27
|
+
};
|
|
28
|
+
/** The backend rejected the serve token (revoked, re-keyed, or the user lost access): log in again. */
|
|
29
|
+
export declare class ServeAuthorizationRejectedError extends Error {
|
|
30
|
+
constructor(message: string);
|
|
31
|
+
}
|
|
32
|
+
/** Non-prod environments the serve token's company can overlay. */
|
|
33
|
+
export declare function listServeEnvironments(backendUrl: string, token: string): Promise<ServeEnvironment[]>;
|
|
34
|
+
export declare function authorizeServe(backendUrl: string, options?: {
|
|
35
|
+
openBrowser?: boolean;
|
|
36
|
+
}): Promise<ServeAuthorization>;
|
|
37
|
+
//# sourceMappingURL=auth.d.ts.map
|
|
@@ -0,0 +1,211 @@
|
|
|
1
|
+
import { spawn } from 'node:child_process';
|
|
2
|
+
import { chmod, mkdir, readFile, rename, writeFile } from 'node:fs/promises';
|
|
3
|
+
import { homedir } from 'node:os';
|
|
4
|
+
import path from 'node:path';
|
|
5
|
+
import { readCompanyRepoConfig } from '../config.js';
|
|
6
|
+
import { toHttpUrl } from './backend-url.js';
|
|
7
|
+
import { copyToClipboard } from './clipboard.js';
|
|
8
|
+
import { apiUrl, getJson, postJson } from './http.js';
|
|
9
|
+
import { progressIndicator } from './progress.js';
|
|
10
|
+
function env(name) {
|
|
11
|
+
const value = process.env[name];
|
|
12
|
+
return value && value.length > 0 ? value : undefined;
|
|
13
|
+
}
|
|
14
|
+
export function resolveBackendUrl(repoRoot, optionUrl) {
|
|
15
|
+
const resolved = optionUrl?.trim() || env('SUPERPANEL_SDK_BACKEND_URL') || readCompanyRepoConfig(repoRoot)?.backendUrl || undefined;
|
|
16
|
+
if (!resolved) {
|
|
17
|
+
throw new Error('No backend URL configured. Pass --url <backend-url>, set SUPERPANEL_SDK_BACKEND_URL, or configure .superpanel/config.json.');
|
|
18
|
+
}
|
|
19
|
+
return resolved;
|
|
20
|
+
}
|
|
21
|
+
function backendCacheKey(backendUrl) {
|
|
22
|
+
const url = toHttpUrl(backendUrl);
|
|
23
|
+
url.pathname = url.pathname.replace(/\/$/, '');
|
|
24
|
+
url.search = '';
|
|
25
|
+
url.hash = '';
|
|
26
|
+
return url.toString();
|
|
27
|
+
}
|
|
28
|
+
function authorizationCachePath() {
|
|
29
|
+
return path.join(homedir(), '.superpanel', 'credentials.json');
|
|
30
|
+
}
|
|
31
|
+
function tokenExpiresAt(token) {
|
|
32
|
+
const body = token.split('.')[1];
|
|
33
|
+
if (!body)
|
|
34
|
+
return null;
|
|
35
|
+
try {
|
|
36
|
+
const claims = JSON.parse(Buffer.from(body, 'base64url').toString('utf8'));
|
|
37
|
+
return claims.purpose === 'serve' && typeof claims.exp === 'number' ? claims.exp * 1000 : null;
|
|
38
|
+
}
|
|
39
|
+
catch {
|
|
40
|
+
return null;
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
function isServeAuthorization(value) {
|
|
44
|
+
if (!value || typeof value !== 'object')
|
|
45
|
+
return false;
|
|
46
|
+
const record = value;
|
|
47
|
+
const user = record.user;
|
|
48
|
+
return (typeof record.token === 'string' &&
|
|
49
|
+
typeof record.companyId === 'string' &&
|
|
50
|
+
(record.companyName === null || typeof record.companyName === 'string') &&
|
|
51
|
+
Boolean(user) &&
|
|
52
|
+
typeof user === 'object' &&
|
|
53
|
+
typeof user.id === 'string' &&
|
|
54
|
+
typeof user.email === 'string');
|
|
55
|
+
}
|
|
56
|
+
async function readAuthorizationCache(file = authorizationCachePath()) {
|
|
57
|
+
try {
|
|
58
|
+
const raw = JSON.parse(await readFile(file, 'utf8'));
|
|
59
|
+
if (raw.version !== 1 || !raw.credentials || typeof raw.credentials !== 'object') {
|
|
60
|
+
return { version: 1, credentials: {} };
|
|
61
|
+
}
|
|
62
|
+
const credentials = {};
|
|
63
|
+
for (const [key, value] of Object.entries(raw.credentials)) {
|
|
64
|
+
if (isServeAuthorization(value))
|
|
65
|
+
credentials[key] = value;
|
|
66
|
+
}
|
|
67
|
+
return { version: 1, credentials };
|
|
68
|
+
}
|
|
69
|
+
catch (error) {
|
|
70
|
+
const code = error && typeof error === 'object' && 'code' in error ? error.code : undefined;
|
|
71
|
+
if (code === 'ENOENT' || error instanceof SyntaxError)
|
|
72
|
+
return { version: 1, credentials: {} };
|
|
73
|
+
throw error;
|
|
74
|
+
}
|
|
75
|
+
}
|
|
76
|
+
async function writeAuthorizationCache(cache, file = authorizationCachePath()) {
|
|
77
|
+
const directory = path.dirname(file);
|
|
78
|
+
await mkdir(directory, { recursive: true, mode: 0o700 });
|
|
79
|
+
const temporary = `${file}.${process.pid}.tmp`;
|
|
80
|
+
await writeFile(temporary, `${JSON.stringify(cache, null, 2)}\n`, { mode: 0o600 });
|
|
81
|
+
await rename(temporary, file);
|
|
82
|
+
await chmod(file, 0o600);
|
|
83
|
+
}
|
|
84
|
+
export async function loadCachedServeAuthorization(backendUrl, file = authorizationCachePath()) {
|
|
85
|
+
const authorization = (await readAuthorizationCache(file)).credentials[backendCacheKey(backendUrl)];
|
|
86
|
+
if (!authorization)
|
|
87
|
+
return null;
|
|
88
|
+
const expiresAt = tokenExpiresAt(authorization.token);
|
|
89
|
+
return expiresAt !== null && expiresAt > Date.now() + 30_000 ? authorization : null;
|
|
90
|
+
}
|
|
91
|
+
export async function cacheServeAuthorization(backendUrl, authorization, file = authorizationCachePath()) {
|
|
92
|
+
const cache = await readAuthorizationCache(file);
|
|
93
|
+
cache.credentials[backendCacheKey(backendUrl)] = authorization;
|
|
94
|
+
await writeAuthorizationCache(cache, file);
|
|
95
|
+
}
|
|
96
|
+
export async function clearCachedServeAuthorization(backendUrl, token, file = authorizationCachePath()) {
|
|
97
|
+
const cache = await readAuthorizationCache(file);
|
|
98
|
+
const key = backendCacheKey(backendUrl);
|
|
99
|
+
if (cache.credentials[key]?.token !== token)
|
|
100
|
+
return;
|
|
101
|
+
delete cache.credentials[key];
|
|
102
|
+
await writeAuthorizationCache(cache, file);
|
|
103
|
+
}
|
|
104
|
+
export async function removeCachedServeAuthorization(backendUrl, file = authorizationCachePath()) {
|
|
105
|
+
const cache = await readAuthorizationCache(file);
|
|
106
|
+
const key = backendCacheKey(backendUrl);
|
|
107
|
+
if (!(key in cache.credentials))
|
|
108
|
+
return false;
|
|
109
|
+
delete cache.credentials[key];
|
|
110
|
+
await writeAuthorizationCache(cache, file);
|
|
111
|
+
return true;
|
|
112
|
+
}
|
|
113
|
+
export async function readServeAuthorizationStatus(backendUrl, file = authorizationCachePath()) {
|
|
114
|
+
const authorization = (await readAuthorizationCache(file)).credentials[backendCacheKey(backendUrl)];
|
|
115
|
+
if (!authorization)
|
|
116
|
+
return { loggedIn: false };
|
|
117
|
+
const expiresAt = tokenExpiresAt(authorization.token);
|
|
118
|
+
if (expiresAt === null)
|
|
119
|
+
return { loggedIn: false };
|
|
120
|
+
return { loggedIn: true, authorization, expiresAt, expired: expiresAt <= Date.now() };
|
|
121
|
+
}
|
|
122
|
+
/** The backend rejected the serve token (revoked, re-keyed, or the user lost access): log in again. */
|
|
123
|
+
export class ServeAuthorizationRejectedError extends Error {
|
|
124
|
+
constructor(message) {
|
|
125
|
+
super(message);
|
|
126
|
+
this.name = 'ServeAuthorizationRejectedError';
|
|
127
|
+
}
|
|
128
|
+
}
|
|
129
|
+
/** Non-prod environments the serve token's company can overlay. */
|
|
130
|
+
export async function listServeEnvironments(backendUrl, token) {
|
|
131
|
+
const { response, payload } = await getJson(apiUrl(backendUrl, 'cli-auth/environments'), token);
|
|
132
|
+
if (response.status === 401) {
|
|
133
|
+
throw new ServeAuthorizationRejectedError(payload.error ?? 'serve authorization rejected');
|
|
134
|
+
}
|
|
135
|
+
if (!response.ok) {
|
|
136
|
+
throw new Error(payload.error ?? `could not list environments (${response.status})`);
|
|
137
|
+
}
|
|
138
|
+
if (!Array.isArray(payload.environments)) {
|
|
139
|
+
throw new Error('malformed response from the backend (missing environments list)');
|
|
140
|
+
}
|
|
141
|
+
return payload.environments;
|
|
142
|
+
}
|
|
143
|
+
function openBrowser(url) {
|
|
144
|
+
const command = process.platform === 'darwin'
|
|
145
|
+
? { file: 'open', args: [url] }
|
|
146
|
+
: process.platform === 'win32'
|
|
147
|
+
? { file: 'rundll32', args: ['url.dll,FileProtocolHandler', url] }
|
|
148
|
+
: { file: 'xdg-open', args: [url] };
|
|
149
|
+
const child = spawn(command.file, command.args, { detached: true, stdio: 'ignore' });
|
|
150
|
+
child.once('error', () => undefined);
|
|
151
|
+
child.unref();
|
|
152
|
+
}
|
|
153
|
+
function wait(ms) {
|
|
154
|
+
return new Promise((resolve) => setTimeout(resolve, ms));
|
|
155
|
+
}
|
|
156
|
+
export async function authorizeServe(backendUrl, options = {}) {
|
|
157
|
+
const starting = progressIndicator();
|
|
158
|
+
starting.start('Starting browser authorization…');
|
|
159
|
+
let created;
|
|
160
|
+
try {
|
|
161
|
+
created = await postJson(apiUrl(backendUrl, 'cli-auth/device'), {});
|
|
162
|
+
if (!created.response.ok) {
|
|
163
|
+
throw new Error(created.payload.error ?? `could not start browser authorization (${created.response.status})`);
|
|
164
|
+
}
|
|
165
|
+
}
|
|
166
|
+
catch (error) {
|
|
167
|
+
starting.error('Could not start browser authorization.');
|
|
168
|
+
throw error;
|
|
169
|
+
}
|
|
170
|
+
starting.stop('Browser authorization started.');
|
|
171
|
+
const device = created.payload;
|
|
172
|
+
const progress = progressIndicator();
|
|
173
|
+
if (options.openBrowser === false) {
|
|
174
|
+
try {
|
|
175
|
+
copyToClipboard(device.verificationUrl);
|
|
176
|
+
progress.start(`Authorization URL copied to your clipboard. Paste it into a browser to continue:\n ${device.verificationUrl}`);
|
|
177
|
+
}
|
|
178
|
+
catch (error) {
|
|
179
|
+
progress.start(`Could not copy the authorization URL (${error instanceof Error ? error.message : String(error)}). Open it manually:\n ${device.verificationUrl}`);
|
|
180
|
+
}
|
|
181
|
+
}
|
|
182
|
+
else {
|
|
183
|
+
openBrowser(device.verificationUrl);
|
|
184
|
+
progress.start(`Opening your browser to continue — if it doesn't open within a few seconds, open this URL yourself:\n ${device.verificationUrl}`);
|
|
185
|
+
}
|
|
186
|
+
try {
|
|
187
|
+
const expiresAt = Date.now() + device.expiresIn * 1000;
|
|
188
|
+
let delaySeconds = Math.max(1, device.interval);
|
|
189
|
+
while (Date.now() < expiresAt) {
|
|
190
|
+
await wait(delaySeconds * 1000);
|
|
191
|
+
const polled = await postJson(apiUrl(backendUrl, 'cli-auth/token'), { deviceCode: device.deviceCode });
|
|
192
|
+
if (polled.response.ok && 'token' in polled.payload) {
|
|
193
|
+
progress.stop('Authorized. You can close the browser tab now.');
|
|
194
|
+
return polled.payload;
|
|
195
|
+
}
|
|
196
|
+
if (polled.response.status === 202 || polled.response.status === 429) {
|
|
197
|
+
delaySeconds = Math.max(1, 'retryAfter' in polled.payload ? (polled.payload.retryAfter ?? delaySeconds) : delaySeconds);
|
|
198
|
+
continue;
|
|
199
|
+
}
|
|
200
|
+
if (polled.response.status === 410)
|
|
201
|
+
throw new Error('browser authorization expired');
|
|
202
|
+
throw new Error(polled.payload.error ?? `browser authorization failed (${polled.response.status})`);
|
|
203
|
+
}
|
|
204
|
+
throw new Error('browser authorization expired');
|
|
205
|
+
}
|
|
206
|
+
catch (error) {
|
|
207
|
+
progress.error('Browser authorization failed.');
|
|
208
|
+
throw error;
|
|
209
|
+
}
|
|
210
|
+
}
|
|
211
|
+
//# sourceMappingURL=auth.js.map
|
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
/** Parses `raw` and rewrites its protocol to the ws/wss equivalent (http→ws, https→wss). */
|
|
2
|
+
export declare function toWsUrl(raw: string): URL;
|
|
3
|
+
/** Parses `raw` and rewrites its protocol to the http/https equivalent (ws→http, wss→https). */
|
|
4
|
+
export declare function toHttpUrl(raw: string): URL;
|
|
5
|
+
//# sourceMappingURL=backend-url.d.ts.map
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
/** Parses `raw` and rewrites its protocol to the ws/wss equivalent (http→ws, https→wss). */
|
|
2
|
+
export function toWsUrl(raw) {
|
|
3
|
+
const url = new URL(raw);
|
|
4
|
+
if (url.protocol === 'http:')
|
|
5
|
+
url.protocol = 'ws:';
|
|
6
|
+
else if (url.protocol === 'https:')
|
|
7
|
+
url.protocol = 'wss:';
|
|
8
|
+
else if (url.protocol !== 'ws:' && url.protocol !== 'wss:') {
|
|
9
|
+
throw new Error(`unsupported backend url: ${raw}`);
|
|
10
|
+
}
|
|
11
|
+
return url;
|
|
12
|
+
}
|
|
13
|
+
/** Parses `raw` and rewrites its protocol to the http/https equivalent (ws→http, wss→https). */
|
|
14
|
+
export function toHttpUrl(raw) {
|
|
15
|
+
const url = new URL(raw);
|
|
16
|
+
if (url.protocol === 'ws:')
|
|
17
|
+
url.protocol = 'http:';
|
|
18
|
+
else if (url.protocol === 'wss:')
|
|
19
|
+
url.protocol = 'https:';
|
|
20
|
+
else if (url.protocol !== 'http:' && url.protocol !== 'https:') {
|
|
21
|
+
throw new Error(`unsupported backend url: ${raw}`);
|
|
22
|
+
}
|
|
23
|
+
return url;
|
|
24
|
+
}
|
|
25
|
+
//# sourceMappingURL=backend-url.js.map
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
export type ClipboardCommand = {
|
|
2
|
+
file: string;
|
|
3
|
+
args: string[];
|
|
4
|
+
};
|
|
5
|
+
/** Native clipboard commands, in preference order for the current platform. */
|
|
6
|
+
export declare function clipboardCommands(platform: NodeJS.Platform): ClipboardCommand[];
|
|
7
|
+
/** Copy text without invoking a shell, so an authorization URL is always treated as plain input. */
|
|
8
|
+
export declare function copyToClipboard(text: string, platform?: NodeJS.Platform): void;
|
|
9
|
+
//# sourceMappingURL=clipboard.d.ts.map
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
import { spawnSync } from 'node:child_process';
|
|
2
|
+
/** Native clipboard commands, in preference order for the current platform. */
|
|
3
|
+
export function clipboardCommands(platform) {
|
|
4
|
+
switch (platform) {
|
|
5
|
+
case 'darwin':
|
|
6
|
+
return [{ file: 'pbcopy', args: [] }];
|
|
7
|
+
case 'win32':
|
|
8
|
+
return [{ file: 'clip.exe', args: [] }];
|
|
9
|
+
case 'linux':
|
|
10
|
+
return [
|
|
11
|
+
{ file: 'wl-copy', args: [] },
|
|
12
|
+
{ file: 'xclip', args: ['-selection', 'clipboard'] },
|
|
13
|
+
{ file: 'xsel', args: ['--clipboard', '--input'] },
|
|
14
|
+
];
|
|
15
|
+
default:
|
|
16
|
+
return [];
|
|
17
|
+
}
|
|
18
|
+
}
|
|
19
|
+
/** Copy text without invoking a shell, so an authorization URL is always treated as plain input. */
|
|
20
|
+
export function copyToClipboard(text, platform = process.platform) {
|
|
21
|
+
const commands = clipboardCommands(platform);
|
|
22
|
+
if (commands.length === 0)
|
|
23
|
+
throw new Error(`clipboard copy is not supported on ${platform}`);
|
|
24
|
+
const failures = [];
|
|
25
|
+
for (const command of commands) {
|
|
26
|
+
const result = spawnSync(command.file, command.args, {
|
|
27
|
+
input: text,
|
|
28
|
+
encoding: 'utf8',
|
|
29
|
+
stdio: ['pipe', 'ignore', 'pipe'],
|
|
30
|
+
});
|
|
31
|
+
if (!result.error && result.status === 0)
|
|
32
|
+
return;
|
|
33
|
+
const detail = result.error?.message ?? (result.stderr.trim() || `exit ${result.status}`);
|
|
34
|
+
failures.push(`${command.file}: ${detail}`);
|
|
35
|
+
}
|
|
36
|
+
throw new Error(failures.join('; '));
|
|
37
|
+
}
|
|
38
|
+
//# sourceMappingURL=clipboard.js.map
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
export declare function apiUrl(backendUrl: string, path: string): string;
|
|
2
|
+
export declare function requestJson<T>(url: string, init: RequestInit): Promise<{
|
|
3
|
+
response: Response;
|
|
4
|
+
payload: T & {
|
|
5
|
+
error?: string;
|
|
6
|
+
};
|
|
7
|
+
}>;
|
|
8
|
+
export declare function postJson<T>(url: string, body: unknown): Promise<{
|
|
9
|
+
response: Response;
|
|
10
|
+
payload: T & {
|
|
11
|
+
error?: string;
|
|
12
|
+
};
|
|
13
|
+
}>;
|
|
14
|
+
export declare function getJson<T>(url: string, token: string): Promise<{
|
|
15
|
+
response: Response;
|
|
16
|
+
payload: T & {
|
|
17
|
+
error?: string;
|
|
18
|
+
};
|
|
19
|
+
}>;
|
|
20
|
+
//# sourceMappingURL=http.d.ts.map
|