@moneysiren/cli 0.1.0-alpha.1 → 0.1.0-alpha.11

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.
Files changed (36) hide show
  1. package/README.md +11 -3
  2. package/dist/apps/cli/src/cli.d.ts +3 -0
  3. package/dist/apps/cli/src/cli.js +18 -2
  4. package/dist/apps/cli/src/commands/install.js +10 -1
  5. package/dist/apps/cli/src/commands/modes.js +16 -11
  6. package/dist/apps/cli/src/commands/runtime.d.ts +5 -0
  7. package/dist/apps/cli/src/commands/runtime.js +366 -0
  8. package/dist/apps/cli/src/desktop-runtime.d.ts +54 -0
  9. package/dist/apps/cli/src/desktop-runtime.js +720 -0
  10. package/dist/apps/cli/src/home.js +27 -0
  11. package/dist/apps/cli/src/postinstall.js +1 -1
  12. package/dist/apps/cli/src/release-installer.d.ts +4 -1
  13. package/dist/apps/cli/src/release-installer.js +47 -8
  14. package/dist/apps/cli/src/runtime-adapter.js +1 -1
  15. package/dist/apps/cli/src/slash.js +27 -0
  16. package/dist/apps/cli/src/version.d.ts +1 -1
  17. package/dist/apps/cli/src/version.js +1 -1
  18. package/dist/packages/config/src/load.js +3 -0
  19. package/dist/packages/config/src/schema.d.ts +3 -0
  20. package/dist/packages/config/src/schema.js +3 -0
  21. package/dist/packages/local-api/src/server.js +1 -1
  22. package/dist/packages/view-model/src/hud-model.d.ts +74 -0
  23. package/dist/packages/view-model/src/hud-model.js +295 -0
  24. package/dist/packages/view-model/src/index.d.ts +5 -2
  25. package/dist/packages/view-model/src/index.js +4 -1
  26. package/dist/packages/view-model/src/notification-preferences-model.d.ts +30 -2
  27. package/dist/packages/view-model/src/notification-preferences-model.js +183 -1
  28. package/dist/packages/view-model/src/notification-preferences.d.ts +1 -1
  29. package/dist/packages/view-model/src/notification-preferences.js +1 -1
  30. package/dist/packages/view-model/src/sync-state.d.ts +47 -0
  31. package/dist/packages/view-model/src/sync-state.js +140 -0
  32. package/dist/packages/view-model/src/usage-progress.d.ts +22 -0
  33. package/dist/packages/view-model/src/usage-progress.js +57 -0
  34. package/dist/packages/view-model/src/view-model.d.ts +22 -0
  35. package/dist/packages/view-model/src/view-model.js +142 -0
  36. package/package.json +3 -2
@@ -10,6 +10,10 @@ export function renderHomeScreen(input) {
10
10
  ` ${theme.command("/doctor")} Check local readiness without printing secrets`,
11
11
  ` ${theme.command("/install")} Choose CLI, web dashboard, and HUD components`,
12
12
  ` ${theme.command("/modes")} Show the CLI, web, and desktop modes`,
13
+ ` ${theme.command("/start")} Start the installed dashboard runtime`,
14
+ ` ${theme.command("/hud")} Start the installed runtime and open HUD`,
15
+ ` ${theme.command("/status")} Show managed web, HUD, and local API runtime status`,
16
+ ` ${theme.command("/stop")} Stop managed web, HUD, and local API runtimes`,
13
17
  ` ${theme.command("/init")} Create local SQLite storage`,
14
18
  ` ${theme.command("/dashboard")} Check the local dashboard API`,
15
19
  ` ${theme.command("/dashboard check")} Same as /dashboard`,
@@ -27,12 +31,21 @@ export function renderHomeScreen(input) {
27
31
  ` ${theme.command("/quit")} Exit the slash prompt`,
28
32
  "",
29
33
  theme.heading("Classic CLI"),
34
+ " msiren start",
35
+ " msiren hud",
36
+ " msiren status",
37
+ " msiren stop",
38
+ " msiren install --all",
39
+ "",
40
+ theme.heading("Full command"),
30
41
  " moneysiren doctor",
31
42
  " moneysiren install",
32
43
  " moneysiren install --status",
33
44
  " moneysiren modes",
34
45
  " moneysiren init",
35
46
  " moneysiren serve [--port <port>]",
47
+ " moneysiren stop [--web|--hud|--api|--all]",
48
+ " moneysiren restart [--port <port>] [--open|--no-open] [--hud]",
36
49
  " moneysiren open",
37
50
  " moneysiren sync --provider mock",
38
51
  " moneysiren summary --json",
@@ -54,6 +67,11 @@ export function renderHelpScreen(version) {
54
67
 
55
68
  Local-first cloud/SaaS usage, status, and expected billing dashboard.
56
69
 
70
+ Short command:
71
+ msiren start
72
+ msiren hud
73
+ msiren install --all
74
+
57
75
  Usage:
58
76
  moneysiren
59
77
  moneysiren --help
@@ -62,6 +80,11 @@ Usage:
62
80
  moneysiren install [--status|--all|--cli|--web|--hud|--no-cli|--no-web|--no-hud]
63
81
  moneysiren doctor
64
82
  moneysiren modes
83
+ moneysiren start [--port <port>] [--open|--no-open] [--hud]
84
+ moneysiren hud [--port <port>]
85
+ moneysiren status
86
+ moneysiren stop [--web|--hud|--api|--all]
87
+ moneysiren restart [--port <port>] [--open|--no-open] [--hud]
65
88
  moneysiren dashboard check [--url <local-dashboard-url>]
66
89
  moneysiren serve [--port <port>]
67
90
  moneysiren open
@@ -81,6 +104,10 @@ Slash commands:
81
104
  moneysiren /doctor
82
105
  moneysiren /install
83
106
  moneysiren /modes
107
+ moneysiren /start
108
+ moneysiren /hud
109
+ moneysiren /status
110
+ moneysiren /stop [--web|--hud|--api|--all]
84
111
  moneysiren /init
85
112
  moneysiren /dashboard
86
113
  moneysiren /dashboard check
@@ -10,7 +10,7 @@ try {
10
10
  });
11
11
  console.log("MoneySiren setup profile saved.");
12
12
  console.log(formatInstallSelectionLine(profile.selectedSurfaces));
13
- console.log("Run `moneysiren install --status` to review or `moneysiren install` to change it.");
13
+ console.log("Run `msiren install --status` to review, `msiren install` to change it, or `msiren start` after installing release assets.");
14
14
  }
15
15
  catch (error) {
16
16
  console.warn(`MoneySiren setup profile skipped: ${error instanceof Error ? error.message : String(error)}`);
@@ -1,6 +1,6 @@
1
1
  import type { InstallSurface } from "./install-profile.js";
2
2
  export declare const DEFAULT_RELEASE_REPOSITORY = "ztwz11/moneysiren";
3
- export declare const DEFAULT_RELEASE_TAG = "v0.1.0-alpha.0";
3
+ export declare const DEFAULT_RELEASE_TAG = "v0.1.0-alpha.11";
4
4
  export interface ReleaseInstallOptions {
5
5
  env?: Record<string, string | undefined>;
6
6
  fetchImpl: typeof fetch;
@@ -28,16 +28,19 @@ export interface InstalledReleaseAsset {
28
28
  sha256: string;
29
29
  checksumVerified: boolean;
30
30
  signatureVerified: boolean;
31
+ signatureStatus: string;
31
32
  }
32
33
  export interface ReleaseAssetSignatureVerifier {
33
34
  verify(input: ReleaseAssetSignatureVerificationInput): Promise<ReleaseAssetSignatureVerificationResult>;
34
35
  }
35
36
  export interface ReleaseAssetSignatureVerificationInput {
36
37
  assetName: string;
38
+ env: Record<string, string | undefined>;
37
39
  expectedSignerThumbprints?: readonly string[];
38
40
  path: string;
39
41
  platform: NodeJS.Platform;
40
42
  surface: Exclude<InstallSurface, "cli">;
43
+ tag: string;
41
44
  }
42
45
  export interface ReleaseAssetSignatureVerificationResult {
43
46
  verified: boolean;
@@ -2,17 +2,18 @@ import { createHash } from "node:crypto";
2
2
  import { execFile } from "node:child_process";
3
3
  import { mkdir, unlink, writeFile } from "node:fs/promises";
4
4
  import { homedir } from "node:os";
5
- import { basename, isAbsolute, join, posix, resolve, win32 } from "node:path";
5
+ import { basename, join, posix, resolve, win32 } from "node:path";
6
6
  import { promisify } from "node:util";
7
7
  const execFileAsync = promisify(execFile);
8
8
  export const DEFAULT_RELEASE_REPOSITORY = "ztwz11/moneysiren";
9
- // The npm CLI can patch faster than desktop assets; keep this pinned until a signed release supersedes it.
10
- export const DEFAULT_RELEASE_TAG = "v0.1.0-alpha.0";
9
+ // Keep the source-free installer pinned to the latest published desktop/web release tag.
10
+ export const DEFAULT_RELEASE_TAG = "v0.1.0-alpha.11";
11
11
  const RELEASE_REPOSITORY_ENV_KEY = "MONEYSIREN_RELEASE_REPOSITORY";
12
12
  const RELEASE_TAG_ENV_KEY = "MONEYSIREN_RELEASE_TAG";
13
13
  const RELEASE_INSTALL_DIR_ENV_KEY = "MONEYSIREN_RELEASE_INSTALL_DIR";
14
14
  const RELEASE_PLATFORM_ENV_KEY = "MONEYSIREN_RELEASE_PLATFORM";
15
15
  const WINDOWS_SIGNER_THUMBPRINTS_ENV_KEY = "MONEYSIREN_WINDOWS_SIGNER_THUMBPRINTS";
16
+ const ALLOW_UNSIGNED_HUD_ENV_KEY = "MONEYSIREN_ALLOW_UNSIGNED_HUD";
16
17
  export async function installReleaseAssets(options) {
17
18
  const env = options.env ?? process.env;
18
19
  const repository = normalizeRepository(options.repository ?? env[RELEASE_REPOSITORY_ENV_KEY] ?? DEFAULT_RELEASE_REPOSITORY);
@@ -65,6 +66,7 @@ export async function installReleaseAssets(options) {
65
66
  platform,
66
67
  releaseAssets,
67
68
  surface,
69
+ tag,
68
70
  ...(options.signatureVerifier === undefined ? {} : { signatureVerifier: options.signatureVerifier }),
69
71
  ...(options.trustedWindowsSignerThumbprints === undefined
70
72
  ? {}
@@ -86,7 +88,8 @@ export async function installReleaseAssets(options) {
86
88
  size: downloaded.byteLength,
87
89
  sha256,
88
90
  checksumVerified: checksum !== null,
89
- signatureVerified: signature.status !== "not-required",
91
+ signatureVerified: isVerifiedSignatureStatus(signature.status),
92
+ signatureStatus: signature.status,
90
93
  });
91
94
  }
92
95
  await writeFile(join(installDir, "install-manifest.json"), `${JSON.stringify({
@@ -104,6 +107,7 @@ export async function installReleaseAssets(options) {
104
107
  sha256: asset.sha256,
105
108
  checksumVerified: asset.checksumVerified,
106
109
  signatureVerified: asset.signatureVerified,
110
+ signatureStatus: asset.signatureStatus,
107
111
  })),
108
112
  }, null, 2)}\n`, "utf8");
109
113
  return {
@@ -118,9 +122,9 @@ export function resolveReleaseInstallDir(input = {}) {
118
122
  const env = input.env ?? process.env;
119
123
  const platform = input.platform ?? process.platform;
120
124
  const tag = input.tag ?? DEFAULT_RELEASE_TAG;
121
- const configured = trimToNull(input.installDir);
125
+ const configured = trimToNull(input.installDir ?? env[RELEASE_INSTALL_DIR_ENV_KEY]);
122
126
  if (configured !== null) {
123
- return isAbsolute(configured) ? configured : resolve(process.cwd(), configured);
127
+ return isAbsoluteForPlatform(platform, configured) ? configured : resolve(process.cwd(), configured);
124
128
  }
125
129
  const root = platform === "win32"
126
130
  ? joinForPlatform(platform, trimToNull(env.APPDATA) ?? win32.join(resolveHomeDirectory(env), "AppData", "Roaming"), "MoneySiren")
@@ -190,13 +194,26 @@ function selectSurfaceAsset(surface, platform, assets) {
190
194
  return candidates.find((asset) => /^moneysiren-web-runtime-.+\.tar\.gz$/i.test(asset.name)) ?? null;
191
195
  }
192
196
  if (platform === "win32") {
193
- return candidates.find((asset) => /\.(exe|msi)$/i.test(asset.name)) ?? null;
197
+ return candidates.find(isDirectWindowsHudAsset) ??
198
+ candidates.find((asset) => isWindowsHudAsset(asset.name)) ??
199
+ null;
194
200
  }
195
201
  if (platform === "darwin") {
196
202
  return candidates.find((asset) => /macos/i.test(asset.name) && /\.(tar\.gz|dmg)$/i.test(asset.name)) ?? null;
197
203
  }
198
204
  return null;
199
205
  }
206
+ function isDirectWindowsHudAsset(asset) {
207
+ return isWindowsHudAsset(asset.name) &&
208
+ /\.exe$/i.test(asset.name) &&
209
+ !isInstallerLikeWindowsAsset(asset.name);
210
+ }
211
+ function isWindowsHudAsset(name) {
212
+ return /\.(exe|msi)$/i.test(name);
213
+ }
214
+ function isInstallerLikeWindowsAsset(name) {
215
+ return /\.msi$/i.test(name) || /(?:^|[._ -])(?:setup|install|installer)(?:[._ -]|$)/i.test(name);
216
+ }
200
217
  async function findChecksum(input) {
201
218
  for (const checksumAsset of input.checksumAssets) {
202
219
  const content = await downloadAsset(input.fetchImpl, checksumAsset.browser_download_url);
@@ -249,10 +266,12 @@ async function verifyReleaseAssetSignature(input) {
249
266
  });
250
267
  return verifier.verify({
251
268
  assetName: input.assetName,
269
+ env: input.env,
252
270
  ...(expectedSignerThumbprints === null ? {} : { expectedSignerThumbprints }),
253
271
  path: input.path,
254
272
  platform: input.platform,
255
273
  surface: input.surface,
274
+ tag: input.tag,
256
275
  });
257
276
  }
258
277
  const defaultReleaseAssetSignatureVerifier = {
@@ -268,10 +287,17 @@ const defaultReleaseAssetSignatureVerifier = {
268
287
  return {
269
288
  verified: false,
270
289
  status: "unsupported",
271
- message: "Windows HUD release assets must be .exe or .msi installers.",
290
+ message: "Windows HUD release assets must be .exe or .msi artifacts.",
272
291
  };
273
292
  }
274
293
  if (input.expectedSignerThumbprints === undefined || input.expectedSignerThumbprints.length === 0) {
294
+ if (isUnsignedPrereleaseHudAllowed(input.env, input.tag)) {
295
+ return {
296
+ verified: true,
297
+ status: "unsigned-prerelease-accepted",
298
+ message: "Unsigned Windows HUD artifact accepted for alpha prerelease.",
299
+ };
300
+ }
275
301
  return {
276
302
  verified: false,
277
303
  status: "missing-signature-metadata",
@@ -306,6 +332,16 @@ async function findExpectedSignerThumbprints(input) {
306
332
  }
307
333
  return null;
308
334
  }
335
+ function isVerifiedSignatureStatus(status) {
336
+ return status !== "not-required" && status !== "unsigned-prerelease-accepted";
337
+ }
338
+ function isUnsignedPrereleaseHudAllowed(env, tag) {
339
+ const configured = env[ALLOW_UNSIGNED_HUD_ENV_KEY]?.trim().toLowerCase();
340
+ if (configured !== undefined && configured.length > 0) {
341
+ return ["1", "true", "yes", "on"].includes(configured);
342
+ }
343
+ return /-(?:alpha|beta|rc)(?:[.\d-]*)?$/i.test(tag);
344
+ }
309
345
  async function verifyWindowsAuthenticodeSignature(path, expectedSignerThumbprints) {
310
346
  const literalPath = powerShellSingleQuotedString(path);
311
347
  try {
@@ -387,6 +423,9 @@ function trimToNull(value) {
387
423
  function joinForPlatform(platform, ...segments) {
388
424
  return platform === "win32" ? win32.join(...segments) : posix.join(...segments);
389
425
  }
426
+ function isAbsoluteForPlatform(platform, value) {
427
+ return platform === "win32" ? win32.isAbsolute(value) : posix.isAbsolute(value);
428
+ }
390
429
  function isRecord(value) {
391
430
  return typeof value === "object" && value !== null && !Array.isArray(value);
392
431
  }
@@ -64,7 +64,7 @@ export async function openUrlInBrowser(url) {
64
64
  throw new Error("Refusing to open a non-loopback runtime URL.");
65
65
  }
66
66
  const child = process.platform === "win32"
67
- ? spawn("cmd", ["/c", "start", "", parsedUrl.toString()], {
67
+ ? spawn("rundll32.exe", ["url.dll,FileProtocolHandler", parsedUrl.toString()], {
68
68
  detached: true,
69
69
  stdio: "ignore",
70
70
  windowsHide: true,
@@ -34,6 +34,33 @@ export function resolveSlashCommand(args) {
34
34
  if (command === "/modes") {
35
35
  return noExtraArgs(command, rest, ["modes"]);
36
36
  }
37
+ if (command === "/start") {
38
+ return {
39
+ kind: "dispatch",
40
+ args: ["start", ...rest],
41
+ };
42
+ }
43
+ if (command === "/hud") {
44
+ return {
45
+ kind: "dispatch",
46
+ args: ["hud", ...rest],
47
+ };
48
+ }
49
+ if (command === "/status") {
50
+ return noExtraArgs(command, rest, ["status"]);
51
+ }
52
+ if (command === "/stop") {
53
+ return {
54
+ kind: "dispatch",
55
+ args: ["stop", ...rest],
56
+ };
57
+ }
58
+ if (command === "/restart") {
59
+ return {
60
+ kind: "dispatch",
61
+ args: ["restart", ...rest],
62
+ };
63
+ }
37
64
  if (command === "/init") {
38
65
  return noExtraArgs(command, rest, ["init"]);
39
66
  }
@@ -1,2 +1,2 @@
1
- export declare const CLI_VERSION = "0.1.0-alpha.1";
1
+ export declare const CLI_VERSION = "0.1.0-alpha.11";
2
2
  //# sourceMappingURL=version.d.ts.map
@@ -1,2 +1,2 @@
1
- export const CLI_VERSION = "0.1.0-alpha.1";
1
+ export const CLI_VERSION = "0.1.0-alpha.11";
2
2
  //# sourceMappingURL=version.js.map
@@ -57,7 +57,10 @@ function loadProviders(env) {
57
57
  datadog: loadProviderConfig("datadog", env),
58
58
  sentry: loadProviderConfig("sentry", env),
59
59
  "codex-cli": loadProviderConfig("codex-cli", env),
60
+ "codex-app": loadProviderConfig("codex-app", env),
60
61
  "claude-cli": loadProviderConfig("claude-cli", env),
62
+ "claude-app": loadProviderConfig("claude-app", env),
63
+ antigravity: loadProviderConfig("antigravity", env),
61
64
  };
62
65
  }
63
66
  function loadProviderConfig(provider, env) {
@@ -20,7 +20,10 @@ export declare const PROVIDER_ENV_KEYS: {
20
20
  readonly datadog: readonly ["DATADOG_API_KEY", "DATADOG_APP_KEY", "DATADOG_SITE"];
21
21
  readonly sentry: readonly ["SENTRY_AUTH_TOKEN", "SENTRY_ORG"];
22
22
  readonly "codex-cli": readonly [];
23
+ readonly "codex-app": readonly [];
23
24
  readonly "claude-cli": readonly [];
25
+ readonly "claude-app": readonly [];
26
+ readonly antigravity: readonly [];
24
27
  };
25
28
  export type ConfiguredProvider = keyof typeof PROVIDER_ENV_KEYS;
26
29
  export interface ProviderConfig {
@@ -20,6 +20,9 @@ export const PROVIDER_ENV_KEYS = {
20
20
  datadog: ["DATADOG_API_KEY", "DATADOG_APP_KEY", "DATADOG_SITE"],
21
21
  sentry: ["SENTRY_AUTH_TOKEN", "SENTRY_ORG"],
22
22
  "codex-cli": [],
23
+ "codex-app": [],
23
24
  "claude-cli": [],
25
+ "claude-app": [],
26
+ antigravity: [],
24
27
  };
25
28
  //# sourceMappingURL=schema.js.map
@@ -3,7 +3,7 @@ import { parseNotificationPreferences, readNotificationDigest, readNotificationP
3
3
  import { assertLoopbackHost, isLoopbackHost, removeRuntimeLock, writeRuntimeLock, } from "../../runtime/src/index.js";
4
4
  const DEFAULT_HOST = "127.0.0.1";
5
5
  const DEFAULT_PORT = 47831;
6
- const DEFAULT_VERSION = "0.1.0-alpha.0";
6
+ const DEFAULT_VERSION = "0.1.0-alpha.11";
7
7
  export async function startLocalApiServer(options = {}) {
8
8
  const host = options.host ?? DEFAULT_HOST;
9
9
  const requestedPort = options.port ?? DEFAULT_PORT;
@@ -0,0 +1,74 @@
1
+ import { type AggregateSyncStatus, type ItemSyncView, type RiskSeverity } from "./sync-state.js";
2
+ import { type UsageProgressView } from "./usage-progress.js";
3
+ import type { TodayLiveProviderView, TodayLiveView } from "./view-model.js";
4
+ import type { NotificationWidgetKey } from "./notification-preferences-model.js";
5
+ export type CreditAccuracy = "exact" | "estimated" | "bounded" | "unknown";
6
+ export interface CreditItemView {
7
+ itemKey: string;
8
+ expiresAt: string | null;
9
+ estimatedEarliestAt: string | null;
10
+ estimatedLatestAt: string | null;
11
+ accuracy: CreditAccuracy;
12
+ status: "active" | "expiring_soon" | "expired" | "unknown";
13
+ }
14
+ export interface CreditPoolView {
15
+ kind: "credit_pool";
16
+ id: string;
17
+ providerKey: "codex-app" | "codex-cli";
18
+ variant: "count" | "expiry";
19
+ availableCount: number | null;
20
+ totalEarnedCount: number | null;
21
+ credits: readonly CreditItemView[];
22
+ unresolvedCount: number;
23
+ nearestExpiryAt: string | null;
24
+ accuracy: CreditAccuracy;
25
+ sync: ItemSyncView;
26
+ riskSeverity: RiskSeverity;
27
+ target: {
28
+ type: "service";
29
+ providerKey: "codex-app" | "codex-cli";
30
+ };
31
+ }
32
+ export interface QuotaItemView {
33
+ kind: "quota";
34
+ id: string;
35
+ providerKey: string;
36
+ window: "five_hour" | "weekly" | "context" | "budget";
37
+ progress: UsageProgressView;
38
+ resetAt: string | null;
39
+ sync: ItemSyncView;
40
+ riskSeverity: RiskSeverity;
41
+ target: {
42
+ type: "service" | "dashboard";
43
+ providerKey?: string;
44
+ routeKey?: string;
45
+ };
46
+ }
47
+ export type HudItemView = QuotaItemView | CreditPoolView;
48
+ export interface HudViewModel {
49
+ generatedAt: string;
50
+ localOnly: true;
51
+ secretsReturned: false;
52
+ dataRevision: string;
53
+ sync: {
54
+ status: AggregateSyncStatus;
55
+ freshCount: number;
56
+ staleCount: number;
57
+ errorCount: number;
58
+ neutralCount: number;
59
+ lastSuccessAt: string | null;
60
+ };
61
+ risk: {
62
+ severity: RiskSeverity;
63
+ warningCount: number;
64
+ criticalCount: number;
65
+ };
66
+ items: readonly HudItemView[];
67
+ }
68
+ export declare const CODEX_APP_PROVIDER_KEY = "codex-app";
69
+ export declare const CODEX_CLI_PROVIDER_KEY = "codex-cli";
70
+ export declare function buildHudViewModel(todayLive: TodayLiveView): HudViewModel;
71
+ export declare function filterHudViewModelByWidgets(model: HudViewModel, selectedWidgets: readonly NotificationWidgetKey[]): HudViewModel;
72
+ export declare function buildCreditPoolFromProvider(provider: TodayLiveProviderView, generatedAt: string): CreditPoolView | null;
73
+ export declare function buildCreditPoolsFromProvider(provider: TodayLiveProviderView, generatedAt: string): CreditPoolView[];
74
+ //# sourceMappingURL=hud-model.d.ts.map