@vellumai/assistant 0.11.1 → 0.11.2-staging.2
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/Dockerfile +1 -3
- package/README.md +1 -1
- package/eslint.config.mjs +28 -8
- package/node_modules/@vellumai/ces-client/node_modules/@vellumai/service-contracts/package.json +1 -0
- package/node_modules/@vellumai/ces-client/node_modules/@vellumai/service-contracts/src/__tests__/stripe-currency.test.ts +37 -0
- package/node_modules/@vellumai/ces-client/node_modules/@vellumai/service-contracts/src/stripe-currency.ts +55 -0
- package/node_modules/@vellumai/gateway-client/node_modules/@vellumai/service-contracts/package.json +1 -0
- package/node_modules/@vellumai/gateway-client/node_modules/@vellumai/service-contracts/src/__tests__/stripe-currency.test.ts +37 -0
- package/node_modules/@vellumai/gateway-client/node_modules/@vellumai/service-contracts/src/stripe-currency.ts +55 -0
- package/node_modules/@vellumai/service-contracts/package.json +1 -0
- package/node_modules/@vellumai/service-contracts/src/__tests__/stripe-currency.test.ts +37 -0
- package/node_modules/@vellumai/service-contracts/src/stripe-currency.ts +55 -0
- package/node_modules/@vellumai/slack-text/src/index.test.ts +48 -0
- package/node_modules/@vellumai/slack-text/src/index.ts +25 -6
- package/openapi.yaml +292 -5
- package/package.json +1 -1
- package/src/__tests__/agent-loop-resume-interrupted.test.ts +223 -0
- package/src/__tests__/byok-default-profile-ensure.test.ts +43 -0
- package/src/__tests__/cli-logger-boundary-guard.test.ts +87 -0
- package/src/__tests__/compaction-events.test.ts +139 -2
- package/src/__tests__/config-loader-backfill.test.ts +35 -3
- package/src/__tests__/config-schema.test.ts +1 -0
- package/src/__tests__/conversation-agent-loop-fatal-cleanup.test.ts +30 -0
- package/src/__tests__/conversation-agent-loop.test.ts +180 -7
- package/src/__tests__/conversation-error.test.ts +211 -4
- package/src/__tests__/conversation-options-turn-scoped-transport.test.ts +64 -0
- package/src/__tests__/conversation-queue.test.ts +126 -0
- package/src/__tests__/conversation-retry-route.test.ts +46 -7
- package/src/__tests__/conversation-slash.test.ts +4 -2
- package/src/__tests__/conversation-summarize-route.test.ts +7 -1
- package/src/__tests__/conversation-summarize-up-to.test.ts +71 -5
- package/src/__tests__/document-create-dedupe.test.ts +2 -1
- package/src/__tests__/document-find-replace.test.ts +2 -1
- package/src/__tests__/document-tool-security.test.ts +2 -1
- package/src/__tests__/document-update-default-surface.test.ts +2 -1
- package/src/__tests__/document-workspace-file.test.ts +467 -0
- package/src/__tests__/emit-signal-routing-intent.test.ts +290 -49
- package/src/__tests__/guardian-card-withdrawal.test.ts +91 -3
- package/src/__tests__/http-user-message-parity.test.ts +66 -0
- package/src/__tests__/list-messages-attachments.test.ts +87 -0
- package/src/__tests__/list-messages-provider-error.test.ts +143 -0
- package/src/__tests__/list-messages-system-card.test.ts +100 -0
- package/src/__tests__/llm-resolver.test.ts +19 -9
- package/src/__tests__/managed-profile-guard.test.ts +23 -0
- package/src/__tests__/notification-platform-adapter.test.ts +130 -2
- package/src/__tests__/notification-telegram-adapter.test.ts +6 -0
- package/src/__tests__/notification-vellum-adapter.test.ts +45 -0
- package/src/__tests__/plugin-api-model-profiles.test.ts +10 -1
- package/src/__tests__/plugin-import-boundary-guard.test.ts +3 -0
- package/src/__tests__/provider-error-scenarios.test.ts +140 -0
- package/src/__tests__/provider-send-message-override-profile.test.ts +95 -0
- package/src/__tests__/run-conversation-turn-persistence.test.ts +66 -3
- package/src/__tests__/scripted-turn-metadata-persistence.test.ts +209 -0
- package/src/__tests__/skills.test.ts +27 -0
- package/src/__tests__/slack-channels-routes.test.ts +0 -2
- package/src/__tests__/slack-share-routes.test.ts +0 -3
- package/src/__tests__/slack-users-routes.test.ts +0 -2
- package/src/__tests__/subagent-tools.test.ts +11 -0
- package/src/__tests__/tool-preview-lifecycle.test.ts +58 -0
- package/src/__tests__/tool-result-spool.test.ts +5 -4
- package/src/__tests__/turn-boundary-resolution.test.ts +53 -0
- package/src/__tests__/turn-events-store.test.ts +26 -0
- package/src/__tests__/ui-visual-surface.test.ts +695 -0
- package/src/__tests__/unified-turn-context-visible-app.test.ts +99 -0
- package/src/__tests__/visible-app-context.test.ts +189 -0
- package/src/__tests__/workspace-git-service.test.ts +173 -33
- package/src/__tests__/workspace-migration-137-repair-retired-fireworks-minimax-model-id.test.ts +157 -0
- package/src/__tests__/workspace-migration-138-backfill-home-feed-titles.test.ts +373 -0
- package/src/__tests__/workspace-migration-139-clear-renamed-cost-profile-label.test.ts +137 -0
- package/src/agent/loop.ts +36 -7
- package/src/api/events/context-window-usage.ts +31 -0
- package/src/api/events/notification-intent.ts +8 -0
- package/src/api/events/ui-surface-pending.ts +35 -0
- package/src/api/index.ts +14 -0
- package/src/api/responses/conversation-message.ts +25 -4
- package/src/api/surfaces.ts +90 -1
- package/src/approvals/guardian-card-withdrawal.ts +66 -31
- package/src/approvals/guardian-decision-primitive.ts +4 -0
- package/src/calls/__tests__/call-setup-router.test.ts +156 -26
- package/src/calls/__tests__/voice-session-bridge.test.ts +201 -12
- package/src/calls/call-setup-router.ts +80 -36
- package/src/calls/voice-session-bridge.ts +173 -26
- package/src/cli/commands/inference.help.ts +3 -3
- package/src/cli/commands/notifications.help.ts +3 -2
- package/src/cli/commands/platform/__tests__/callback-routes-list.test.ts +42 -128
- package/src/cli/commands/platform/__tests__/credits.test.ts +9 -78
- package/src/cli/commands/platform/__tests__/helpers.ts +90 -0
- package/src/cli/commands/platform/__tests__/invoices.test.ts +238 -0
- package/src/cli/commands/platform/__tests__/plans.test.ts +9 -88
- package/src/cli/commands/platform/__tests__/status.test.ts +12 -87
- package/src/cli/commands/platform/__tests__/subscription.test.ts +9 -86
- package/src/cli/commands/platform/index.help.ts +92 -0
- package/src/cli/commands/platform/index.ts +7 -0
- package/src/cli/commands/platform/invoices.ts +132 -0
- package/src/cli/commands/usage.help.ts +1 -1
- package/src/cli/lib/list-installed-plugins.ts +2 -1
- package/src/config/__tests__/default-profile-catalog.test.ts +7 -10
- package/src/config/__tests__/deployment-context-defaults.test.ts +27 -5
- package/src/config/assistant-feature-flags.ts +7 -2
- package/src/config/bundled-skills/app-builder/SKILL.md +3 -2
- package/src/config/bundled-skills/subagent/SKILL.md +3 -1
- package/src/config/bundled-skills/subagent/TOOLS.json +3 -3
- package/src/config/bundled-skills/visualize/SKILL.md +163 -0
- package/src/config/call-site-defaults.ts +3 -2
- package/src/config/default-profile-catalog.ts +75 -70
- package/src/config/default-profile-names.ts +13 -28
- package/src/config/env-registry.ts +1 -0
- package/src/config/feature-flag-registry.json +16 -0
- package/src/config/llm-resolver.ts +21 -5
- package/src/config/loader.ts +18 -11
- package/src/config/schemas/llm.ts +1 -9
- package/src/config/schemas/memory-retrospective.ts +9 -0
- package/src/config/schemas/monitoring.ts +28 -2
- package/src/config/schemas/workspace-git.ts +15 -0
- package/src/config/seed-inference-profiles.ts +19 -5
- package/src/context/post-turn-tool-result-truncation.ts +2 -2
- package/src/conversations/__tests__/message-consolidation.test.ts +54 -0
- package/src/conversations/message-consolidation.ts +17 -15
- package/src/daemon/__tests__/turn-tail-assistant-reply-notify.test.ts +182 -0
- package/src/daemon/__tests__/turn-tail-deleted-conversation.test.ts +181 -0
- package/src/daemon/conversation-agent-loop-handlers.ts +90 -7
- package/src/daemon/conversation-agent-loop.ts +107 -22
- package/src/daemon/conversation-error.ts +169 -48
- package/src/daemon/conversation-messaging.ts +87 -4
- package/src/daemon/conversation-process.ts +57 -45
- package/src/daemon/conversation-runtime-assembly.ts +57 -0
- package/src/daemon/conversation-store.ts +36 -1
- package/src/daemon/conversation-surfaces.ts +68 -6
- package/src/daemon/conversation-turn-finalize.ts +76 -18
- package/src/daemon/conversation.ts +122 -27
- package/src/daemon/lifecycle.ts +9 -0
- package/src/daemon/message-types/conversations.ts +8 -0
- package/src/daemon/message-types/surfaces.ts +3 -0
- package/src/documents/document-store.ts +247 -10
- package/src/home/__tests__/feed-types.test.ts +43 -8
- package/src/home/__tests__/feed-writer.test.ts +59 -0
- package/src/home/feed-types.ts +34 -7
- package/src/home/feed-writer.ts +15 -6
- package/src/live-voice/__tests__/activity-label.test.ts +95 -0
- package/src/live-voice/__tests__/live-activity-reporter.test.ts +86 -5
- package/src/live-voice/__tests__/live-voice-agent-turn.test.ts +310 -25
- package/src/live-voice/__tests__/live-voice-events.test.ts +4 -4
- package/src/live-voice/__tests__/live-voice-triage-escalate.test.ts +8 -4
- package/src/live-voice/__tests__/live-voice-vad.test.ts +9 -1
- package/src/live-voice/activity-label.ts +169 -0
- package/src/live-voice/live-activity-reporter.ts +35 -6
- package/src/live-voice/live-voice-session.ts +282 -41
- package/src/live-voice/protocol.ts +35 -0
- package/src/messaging/providers/slack/__tests__/adapter-mention-rendering.test.ts +84 -2
- package/src/messaging/providers/slack/adapter.ts +86 -19
- package/src/messaging/providers/slack/api.test.ts +85 -1
- package/src/messaging/providers/slack/api.ts +121 -288
- package/src/messaging/providers/slack/client.ts +20 -251
- package/src/messaging/providers/slack/send.test.ts +4 -9
- package/src/messaging/providers/slack/send.ts +1 -1
- package/src/messaging/providers/slack/types.ts +5 -0
- package/src/messaging/providers/slack/web-api-transport.test.ts +181 -0
- package/src/messaging/providers/slack/web-api-transport.ts +367 -0
- package/src/messaging/providers/slack/withdraw.ts +6 -14
- package/src/messaging/providers/telegram-bot/send.test.ts +31 -1
- package/src/messaging/providers/telegram-bot/send.ts +23 -2
- package/src/messaging/providers/telegram-bot/withdraw.test.ts +152 -0
- package/src/messaging/providers/telegram-bot/withdraw.ts +166 -0
- package/src/monitoring/__tests__/db-integrity-sample.test.ts +18 -4
- package/src/monitoring/__tests__/file-descriptors.test.ts +144 -0
- package/src/monitoring/file-descriptors.ts +262 -0
- package/src/monitoring/process-memory.ts +4 -11
- package/src/monitoring/resource-sampler.ts +5 -0
- package/src/monitoring/worker.ts +12 -0
- package/src/notifications/__tests__/assistant-reply-producer.test.ts +740 -0
- package/src/notifications/__tests__/broadcaster.test.ts +347 -7
- package/src/notifications/__tests__/copy-composer.test.ts +53 -3
- package/src/notifications/__tests__/decision-engine.test.ts +349 -29
- package/src/notifications/__tests__/deterministic-checks.test.ts +21 -0
- package/src/notifications/__tests__/edit-notification.test.ts +330 -0
- package/src/notifications/__tests__/guardian-delivery-recorder.test.ts +71 -0
- package/src/notifications/__tests__/home-feed-side-effect.test.ts +170 -12
- package/src/notifications/adapters/macos.ts +3 -0
- package/src/notifications/adapters/platform.ts +90 -14
- package/src/notifications/adapters/telegram.ts +6 -4
- package/src/notifications/assistant-reply-producer.ts +219 -0
- package/src/notifications/broadcaster.ts +523 -317
- package/src/notifications/copy-composer.ts +27 -6
- package/src/notifications/decision-engine.ts +154 -77
- package/src/notifications/deterministic-checks.ts +6 -7
- package/src/notifications/edit-notification.ts +8 -4
- package/src/notifications/emit-signal.ts +49 -16
- package/src/notifications/guardian-delivery-recorder.ts +8 -2
- package/src/notifications/home-feed-side-effect.ts +43 -6
- package/src/notifications/notification-utils.ts +39 -4
- package/src/notifications/signal.ts +10 -0
- package/src/notifications/types.ts +17 -0
- package/src/persistence/bookmark-crud.ts +18 -9
- package/src/persistence/conversation-attention-store.ts +26 -0
- package/src/persistence/conversation-crud.ts +79 -32
- package/src/persistence/conversation-queries.ts +30 -15
- package/src/persistence/conversation-title-service.ts +5 -170
- package/src/persistence/conversation-types.ts +192 -1
- package/src/persistence/db-init.ts +14 -1
- package/src/persistence/db-maintenance.ts +5 -4
- package/src/persistence/embeddings/__tests__/plugin-index-qdrant-init.test.ts +219 -0
- package/src/persistence/embeddings/__tests__/plugin-index.test.ts +22 -0
- package/src/persistence/embeddings/__tests__/worker-script-version.test.ts +76 -0
- package/src/persistence/embeddings/embedding-local.ts +2 -1
- package/src/persistence/embeddings/embedding-runtime-manager.ts +29 -5
- package/src/persistence/embeddings/plugin-index.ts +84 -12
- package/src/persistence/migrations/360-add-document-workspace-path.test.ts +110 -0
- package/src/persistence/migrations/360-add-document-workspace-path.ts +39 -0
- package/src/persistence/planner-statistics.ts +14 -0
- package/src/persistence/schema/documents.ts +26 -11
- package/src/persistence/steps.ts +2 -0
- package/src/platform/client.test.ts +172 -2
- package/src/platform/client.ts +178 -55
- package/src/plugin-api/conversation-turn.ts +13 -0
- package/src/plugin-api/model-profiles.test.ts +6 -2
- package/src/plugins/defaults/memory/__tests__/bookmark-crud.test.ts +34 -0
- package/src/plugins/defaults/memory/__tests__/conversation-queries.test.ts +22 -0
- package/src/plugins/defaults/memory/__tests__/jobs-store-enqueue-gate.test.ts +11 -0
- package/src/plugins/defaults/memory/__tests__/memory-retrospective-accounting.test.ts +199 -0
- package/src/plugins/defaults/memory/__tests__/memory-retrospective-enqueue.test.ts +98 -1
- package/src/plugins/defaults/memory/__tests__/memory-retrospective-job.test.ts +105 -1
- package/src/plugins/defaults/memory/__tests__/memory-retrospective-sweep.test.ts +24 -2
- package/src/plugins/defaults/memory/__tests__/memory-retrospective-wake-chain.test.ts +524 -0
- package/src/plugins/defaults/memory/__tests__/memory-tier-boundary-guard.test.ts +1 -1
- package/src/plugins/defaults/memory/graph/__tests__/conversation-graph-memory-v2-routing.test.ts +9 -4
- package/src/plugins/defaults/memory/host-utils.ts +5 -0
- package/src/plugins/defaults/memory/memory-retrospective-accounting.ts +105 -1
- package/src/plugins/defaults/memory/memory-retrospective-enqueue.ts +64 -5
- package/src/plugins/defaults/memory/memory-retrospective-job.ts +36 -1
- package/src/plugins/defaults/memory/memory-retrospective-sweep.ts +12 -3
- package/src/plugins/defaults/memory/src/__tests__/memory-v2-simulate-route.test.ts +1 -0
- package/src/plugins/defaults/memory/substrate/__tests__/skill-store.test.ts +195 -1
- package/src/plugins/defaults/memory/substrate/consolidation-job.ts +1 -1
- package/src/plugins/defaults/memory/substrate/skill-store.ts +167 -29
- package/src/plugins/defaults/memory/v2/__tests__/injection.test.ts +131 -0
- package/src/plugins/defaults/memory/v2/__tests__/router.test.ts +1 -0
- package/src/plugins/defaults/memory/v2/activation-log-store.ts +4 -0
- package/src/plugins/defaults/memory/v2/injection.ts +48 -3
- package/src/plugins/defaults/memory/v2/rerank-local.ts +6 -2
- package/src/plugins/defaults/memory/v3/__tests__/pool-select.test.ts +1 -1
- package/src/plugins/defaults/platform-hosted/routes/reengage.ts +1 -1
- package/src/plugins/defaults/turn-context/injectors.ts +1 -0
- package/src/plugins/defaults/turn-context/unified-turn-context.ts +26 -0
- package/src/plugins/types.ts +17 -0
- package/src/prompts/templates/system-sections.ts +2 -2
- package/src/providers/__tests__/dispatch-connection-routing.test.ts +43 -2
- package/src/providers/__tests__/vellum-mismatch-routing.test.ts +8 -0
- package/src/providers/call-site-routing.ts +77 -14
- package/src/providers/connection-resolution.ts +42 -2
- package/src/providers/inference/__tests__/adapter-factory-openai-compatible.test.ts +127 -1
- package/src/providers/inference/adapter-factory.ts +70 -12
- package/src/providers/model-catalog.ts +0 -11
- package/src/providers/model-intents.ts +11 -0
- package/src/providers/registry.ts +8 -0
- package/src/providers/retry.ts +85 -17
- package/src/providers/types.ts +8 -1
- package/src/runtime/__tests__/agent-wake.test.ts +181 -0
- package/src/runtime/agent-wake.ts +132 -9
- package/src/runtime/channel-approval-types.ts +25 -0
- package/src/runtime/routes/__tests__/connection-routes-vs-cli-parity.test.ts +2 -2
- package/src/runtime/routes/__tests__/conversation-query-routes.test.ts +11 -0
- package/src/runtime/routes/__tests__/inference-provider-connection-routes.test.ts +121 -9
- package/src/runtime/routes/__tests__/platform-invoice-routes.test.ts +392 -0
- package/src/runtime/routes/__tests__/slack-channel-routes.test.ts +7 -0
- package/src/runtime/routes/__tests__/workspace-commit-routes.test.ts +63 -0
- package/src/runtime/routes/consolidation-routes.ts +1 -1
- package/src/runtime/routes/conversation-management-routes.ts +18 -6
- package/src/runtime/routes/conversation-query-routes.ts +14 -23
- package/src/runtime/routes/conversation-routes.ts +113 -8
- package/src/runtime/routes/credential-routes.ts +1 -5
- package/src/runtime/routes/documents-routes.ts +207 -12
- package/src/runtime/routes/identity-routes.ts +3 -93
- package/src/runtime/routes/inbound-stages/admission-policy.test.ts +31 -1
- package/src/runtime/routes/inbound-stages/admission-policy.ts +18 -0
- package/src/runtime/routes/inference-provider-connection-routes.ts +52 -3
- package/src/runtime/routes/notification-routes.ts +4 -1
- package/src/runtime/routes/platform-routes.ts +238 -3
- package/src/runtime/routes/playground/guard.ts +1 -2
- package/src/runtime/routes/slack-channel-routes.ts +2 -4
- package/src/runtime/routes/workspace-commit-routes.ts +49 -2
- package/src/runtime/routes/workspace-routes.ts +11 -12
- package/src/runtime/routes/workspace-utils.ts +49 -2
- package/src/runtime/services/conversation-serializer.ts +2 -4
- package/src/subagent/__tests__/consult-context-gating.test.ts +98 -0
- package/src/subagent/__tests__/consult-context-skills.test.ts +82 -0
- package/src/subagent/__tests__/consult-context.test.ts +84 -0
- package/src/subagent/__tests__/consult-prompt.test.ts +36 -0
- package/src/subagent/consult-context.ts +410 -0
- package/src/subagent/consult-prompt.ts +38 -5
- package/src/subagent/manager.ts +9 -4
- package/src/subagent/types.ts +8 -0
- package/src/telemetry/telemetry-event-sources.ts +7 -0
- package/src/telemetry/telemetry-wire-source.json +1 -1
- package/src/telemetry/telemetry-wire.generated.ts +1 -0
- package/src/telemetry/turn-events-store.ts +30 -1
- package/src/telemetry/types.ts +24 -0
- package/src/telemetry/usage-telemetry-reporter.test.ts +48 -0
- package/src/tools/browser/pinned-tabs.ts +3 -1
- package/src/tools/subagent/spawn.ts +23 -1
- package/src/tools/terminal/safe-env.ts +1 -0
- package/src/tools/ui-surface/definitions.ts +39 -1
- package/src/tools/ui-surface/surface-shape-docs.ts +38 -4
- package/src/tools/ui-surface/visual-validation.ts +787 -0
- package/src/tools/workflows/run-workflow.test.ts +1 -0
- package/src/util/__tests__/short-title.test.ts +229 -0
- package/src/util/__tests__/worker-compute.test.ts +65 -0
- package/src/util/cgroup-cpu.ts +93 -0
- package/src/util/errors.ts +27 -0
- package/src/util/process-tree.ts +19 -0
- package/src/util/short-title.ts +189 -0
- package/src/util/worker-compute.ts +83 -0
- package/src/workspace/byok-default-profile-ensure.ts +25 -15
- package/src/workspace/git-service.ts +79 -30
- package/src/workspace/migrations/137-repair-retired-fireworks-minimax-model-id.ts +131 -0
- package/src/workspace/migrations/138-backfill-home-feed-titles.ts +179 -0
- package/src/workspace/migrations/139-clear-renamed-cost-profile-label.ts +92 -0
- package/src/workspace/migrations/registry.ts +6 -0
- package/src/plugins/defaults/memory/substrate/constants.ts +0 -8
|
@@ -1,79 +1,13 @@
|
|
|
1
|
-
import { beforeEach, describe, expect,
|
|
1
|
+
import { beforeEach, describe, expect, test } from "bun:test";
|
|
2
2
|
|
|
3
|
-
import {
|
|
3
|
+
import { runPlatform, setupPlatformIpcMock } from "./helpers.js";
|
|
4
4
|
|
|
5
|
-
|
|
6
|
-
// Mock state
|
|
7
|
-
// ---------------------------------------------------------------------------
|
|
8
|
-
|
|
9
|
-
let mockCalls: Array<[string, Record<string, unknown>]> = [];
|
|
10
|
-
let mockResponse: unknown = {
|
|
11
|
-
ok: true,
|
|
12
|
-
result: {
|
|
13
|
-
plans: [
|
|
14
|
-
{
|
|
15
|
-
id: "base",
|
|
16
|
-
name: "Base",
|
|
17
|
-
price_cents: 0,
|
|
18
|
-
billing_interval: "month",
|
|
19
|
-
included_features: ["Pay-as-you-go credits"],
|
|
20
|
-
},
|
|
21
|
-
{
|
|
22
|
-
id: "pro",
|
|
23
|
-
name: "Pro",
|
|
24
|
-
base_price_cents: 2000,
|
|
25
|
-
billing_interval: "month",
|
|
26
|
-
included_features: ["Configurable machine size"],
|
|
27
|
-
},
|
|
28
|
-
],
|
|
29
|
-
},
|
|
30
|
-
};
|
|
31
|
-
|
|
32
|
-
// ---------------------------------------------------------------------------
|
|
33
|
-
// Mocks
|
|
34
|
-
// ---------------------------------------------------------------------------
|
|
35
|
-
|
|
36
|
-
mock.module("../../../../ipc/cli-client.js", () => ({
|
|
37
|
-
cliIpcCall: async (method: string, params: Record<string, unknown>) => {
|
|
38
|
-
mockCalls.push([method, params]);
|
|
39
|
-
return mockResponse;
|
|
40
|
-
},
|
|
41
|
-
exitFromIpcResult: (_r: unknown, _cmd: unknown) => {
|
|
42
|
-
throw new Error("exitFromIpcResult called");
|
|
43
|
-
},
|
|
44
|
-
}));
|
|
45
|
-
|
|
46
|
-
const { registerPlatformCommand } = await import("../index.js");
|
|
47
|
-
|
|
48
|
-
function buildProgram(): Command {
|
|
49
|
-
const program = new Command();
|
|
50
|
-
program.exitOverride();
|
|
51
|
-
registerPlatformCommand(program);
|
|
52
|
-
return program;
|
|
53
|
-
}
|
|
54
|
-
|
|
55
|
-
function captureStdout(fn: () => Promise<void>): Promise<string[]> {
|
|
56
|
-
const chunks: string[] = [];
|
|
57
|
-
const origWrite = process.stdout.write.bind(process.stdout);
|
|
58
|
-
process.stdout.write = ((chunk: unknown) => {
|
|
59
|
-
chunks.push(typeof chunk === "string" ? chunk : String(chunk));
|
|
60
|
-
return true;
|
|
61
|
-
}) as typeof process.stdout.write;
|
|
62
|
-
return fn()
|
|
63
|
-
.then(() => chunks)
|
|
64
|
-
.finally(() => {
|
|
65
|
-
process.stdout.write = origWrite;
|
|
66
|
-
});
|
|
67
|
-
}
|
|
68
|
-
|
|
69
|
-
// ---------------------------------------------------------------------------
|
|
70
|
-
// Tests
|
|
71
|
-
// ---------------------------------------------------------------------------
|
|
5
|
+
const ipc = setupPlatformIpcMock();
|
|
72
6
|
|
|
73
7
|
describe("assistant platform plans", () => {
|
|
74
8
|
beforeEach(() => {
|
|
75
|
-
|
|
76
|
-
|
|
9
|
+
ipc.calls = [];
|
|
10
|
+
ipc.response = {
|
|
77
11
|
ok: true,
|
|
78
12
|
result: {
|
|
79
13
|
plans: [
|
|
@@ -94,22 +28,12 @@ describe("assistant platform plans", () => {
|
|
|
94
28
|
],
|
|
95
29
|
},
|
|
96
30
|
};
|
|
97
|
-
process.exitCode = 0;
|
|
98
31
|
});
|
|
99
32
|
|
|
100
33
|
test("calls platform_plans and emits catalog JSON with --json", async () => {
|
|
101
|
-
const out = await
|
|
102
|
-
const program = buildProgram();
|
|
103
|
-
await program.parseAsync([
|
|
104
|
-
"node",
|
|
105
|
-
"assistant",
|
|
106
|
-
"platform",
|
|
107
|
-
"plans",
|
|
108
|
-
"--json",
|
|
109
|
-
]);
|
|
110
|
-
});
|
|
34
|
+
const out = await runPlatform(["plans", "--json"]);
|
|
111
35
|
|
|
112
|
-
expect(
|
|
36
|
+
expect(ipc.calls[0][0]).toBe("platform_plans");
|
|
113
37
|
|
|
114
38
|
const parsed = JSON.parse(out.join(""));
|
|
115
39
|
expect(parsed.plans).toHaveLength(2);
|
|
@@ -118,12 +42,9 @@ describe("assistant platform plans", () => {
|
|
|
118
42
|
});
|
|
119
43
|
|
|
120
44
|
test("plain text mode does not emit JSON to stdout", async () => {
|
|
121
|
-
const out = await
|
|
122
|
-
const program = buildProgram();
|
|
123
|
-
await program.parseAsync(["node", "assistant", "platform", "plans"]);
|
|
124
|
-
});
|
|
45
|
+
const out = await runPlatform(["plans"]);
|
|
125
46
|
|
|
126
|
-
// Plain-text mode logs via log.info
|
|
47
|
+
// Plain-text mode logs via log.info; verify writeOutput (JSON) was NOT called
|
|
127
48
|
expect(() => JSON.parse(out.join("").trim())).toThrow();
|
|
128
49
|
});
|
|
129
50
|
});
|
|
@@ -1,57 +1,13 @@
|
|
|
1
|
-
import { beforeEach, describe, expect,
|
|
1
|
+
import { beforeEach, describe, expect, test } from "bun:test";
|
|
2
2
|
|
|
3
|
-
import {
|
|
3
|
+
import { runPlatform, setupPlatformIpcMock } from "./helpers.js";
|
|
4
4
|
|
|
5
|
-
|
|
6
|
-
// Mock state
|
|
7
|
-
// ---------------------------------------------------------------------------
|
|
8
|
-
|
|
9
|
-
let mockCalls: Array<[string, Record<string, unknown>]> = [];
|
|
10
|
-
let mockResponse: unknown = {
|
|
11
|
-
ok: true,
|
|
12
|
-
result: {
|
|
13
|
-
isPlatform: false,
|
|
14
|
-
baseUrl: "",
|
|
15
|
-
assistantId: "",
|
|
16
|
-
hasAssistantApiKey: false,
|
|
17
|
-
hasWebhookSecret: false,
|
|
18
|
-
available: false,
|
|
19
|
-
organizationId: null,
|
|
20
|
-
userId: null,
|
|
21
|
-
},
|
|
22
|
-
};
|
|
23
|
-
|
|
24
|
-
// ---------------------------------------------------------------------------
|
|
25
|
-
// Mocks
|
|
26
|
-
// ---------------------------------------------------------------------------
|
|
27
|
-
|
|
28
|
-
mock.module("../../../../ipc/cli-client.js", () => ({
|
|
29
|
-
cliIpcCall: async (method: string, params: Record<string, unknown>) => {
|
|
30
|
-
mockCalls.push([method, params]);
|
|
31
|
-
return mockResponse;
|
|
32
|
-
},
|
|
33
|
-
exitFromIpcResult: (_r: unknown, _cmd: unknown) => {
|
|
34
|
-
throw new Error("exitFromIpcResult called");
|
|
35
|
-
},
|
|
36
|
-
}));
|
|
37
|
-
|
|
38
|
-
const { registerPlatformCommand } = await import("../index.js");
|
|
39
|
-
|
|
40
|
-
function buildProgram(): Command {
|
|
41
|
-
const program = new Command();
|
|
42
|
-
program.exitOverride();
|
|
43
|
-
registerPlatformCommand(program);
|
|
44
|
-
return program;
|
|
45
|
-
}
|
|
46
|
-
|
|
47
|
-
// ---------------------------------------------------------------------------
|
|
48
|
-
// Tests
|
|
49
|
-
// ---------------------------------------------------------------------------
|
|
5
|
+
const ipc = setupPlatformIpcMock();
|
|
50
6
|
|
|
51
7
|
describe("assistant platform status", () => {
|
|
52
8
|
beforeEach(() => {
|
|
53
|
-
|
|
54
|
-
|
|
9
|
+
ipc.calls = [];
|
|
10
|
+
ipc.response = {
|
|
55
11
|
ok: true,
|
|
56
12
|
result: {
|
|
57
13
|
isPlatform: false,
|
|
@@ -64,11 +20,10 @@ describe("assistant platform status", () => {
|
|
|
64
20
|
userId: null,
|
|
65
21
|
},
|
|
66
22
|
};
|
|
67
|
-
process.exitCode = 0;
|
|
68
23
|
});
|
|
69
24
|
|
|
70
25
|
test("platform pod returns full status from context", async () => {
|
|
71
|
-
|
|
26
|
+
ipc.response = {
|
|
72
27
|
ok: true,
|
|
73
28
|
result: {
|
|
74
29
|
isPlatform: true,
|
|
@@ -82,29 +37,11 @@ describe("assistant platform status", () => {
|
|
|
82
37
|
},
|
|
83
38
|
};
|
|
84
39
|
|
|
85
|
-
const
|
|
86
|
-
const stdoutChunks: string[] = [];
|
|
87
|
-
const origWrite = process.stdout.write.bind(process.stdout);
|
|
88
|
-
process.stdout.write = ((chunk: unknown) => {
|
|
89
|
-
stdoutChunks.push(typeof chunk === "string" ? chunk : String(chunk));
|
|
90
|
-
return true;
|
|
91
|
-
}) as typeof process.stdout.write;
|
|
40
|
+
const out = await runPlatform(["status", "--json"]);
|
|
92
41
|
|
|
93
|
-
|
|
94
|
-
await program.parseAsync([
|
|
95
|
-
"node",
|
|
96
|
-
"assistant",
|
|
97
|
-
"platform",
|
|
98
|
-
"status",
|
|
99
|
-
"--json",
|
|
100
|
-
]);
|
|
101
|
-
} finally {
|
|
102
|
-
process.stdout.write = origWrite;
|
|
103
|
-
}
|
|
42
|
+
expect(ipc.calls[0][0]).toBe("platform_status");
|
|
104
43
|
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
const parsed = JSON.parse(stdoutChunks.join(""));
|
|
44
|
+
const parsed = JSON.parse(out.join(""));
|
|
108
45
|
expect(parsed.isPlatform).toBe(true);
|
|
109
46
|
expect(parsed.baseUrl).toBe("https://platform.vellum.ai");
|
|
110
47
|
expect(parsed.assistantId).toBe("asst-abc-123");
|
|
@@ -116,21 +53,9 @@ describe("assistant platform status", () => {
|
|
|
116
53
|
});
|
|
117
54
|
|
|
118
55
|
test("plain text mode does not emit JSON to stdout", async () => {
|
|
119
|
-
const
|
|
120
|
-
const stdoutChunks: string[] = [];
|
|
121
|
-
const origWrite = process.stdout.write.bind(process.stdout);
|
|
122
|
-
process.stdout.write = ((chunk: unknown) => {
|
|
123
|
-
stdoutChunks.push(typeof chunk === "string" ? chunk : String(chunk));
|
|
124
|
-
return true;
|
|
125
|
-
}) as typeof process.stdout.write;
|
|
126
|
-
|
|
127
|
-
try {
|
|
128
|
-
await program.parseAsync(["node", "assistant", "platform", "status"]);
|
|
129
|
-
} finally {
|
|
130
|
-
process.stdout.write = origWrite;
|
|
131
|
-
}
|
|
56
|
+
const out = await runPlatform(["status"]);
|
|
132
57
|
|
|
133
|
-
// Plain-text mode logs via log.info
|
|
134
|
-
expect(() => JSON.parse(
|
|
58
|
+
// Plain-text mode logs via log.info; verify writeOutput (JSON) was NOT called
|
|
59
|
+
expect(() => JSON.parse(out.join("").trim())).toThrow();
|
|
135
60
|
});
|
|
136
61
|
});
|
|
@@ -1,72 +1,13 @@
|
|
|
1
|
-
import { beforeEach, describe, expect,
|
|
1
|
+
import { beforeEach, describe, expect, test } from "bun:test";
|
|
2
2
|
|
|
3
|
-
import {
|
|
3
|
+
import { runPlatform, setupPlatformIpcMock } from "./helpers.js";
|
|
4
4
|
|
|
5
|
-
|
|
6
|
-
// Mock state
|
|
7
|
-
// ---------------------------------------------------------------------------
|
|
8
|
-
|
|
9
|
-
let mockCalls: Array<[string, Record<string, unknown>]> = [];
|
|
10
|
-
let mockResponse: unknown = {
|
|
11
|
-
ok: true,
|
|
12
|
-
result: {
|
|
13
|
-
planId: "pro",
|
|
14
|
-
status: "active",
|
|
15
|
-
renewalDate: "2026-08-01T00:00:00.000Z",
|
|
16
|
-
currentPeriodEnd: "2026-08-01T00:00:00.000Z",
|
|
17
|
-
cancelAtPeriodEnd: false,
|
|
18
|
-
cancelAt: null,
|
|
19
|
-
selectedCreditTier: "credits_50",
|
|
20
|
-
package: { key: "super", name: "Super", version: 2, customized: false },
|
|
21
|
-
entitlements: { managedEmail: true, phoneNumber: false },
|
|
22
|
-
},
|
|
23
|
-
};
|
|
24
|
-
|
|
25
|
-
// ---------------------------------------------------------------------------
|
|
26
|
-
// Mocks
|
|
27
|
-
// ---------------------------------------------------------------------------
|
|
28
|
-
|
|
29
|
-
mock.module("../../../../ipc/cli-client.js", () => ({
|
|
30
|
-
cliIpcCall: async (method: string, params: Record<string, unknown>) => {
|
|
31
|
-
mockCalls.push([method, params]);
|
|
32
|
-
return mockResponse;
|
|
33
|
-
},
|
|
34
|
-
exitFromIpcResult: (_r: unknown, _cmd: unknown) => {
|
|
35
|
-
throw new Error("exitFromIpcResult called");
|
|
36
|
-
},
|
|
37
|
-
}));
|
|
38
|
-
|
|
39
|
-
const { registerPlatformCommand } = await import("../index.js");
|
|
40
|
-
|
|
41
|
-
function buildProgram(): Command {
|
|
42
|
-
const program = new Command();
|
|
43
|
-
program.exitOverride();
|
|
44
|
-
registerPlatformCommand(program);
|
|
45
|
-
return program;
|
|
46
|
-
}
|
|
47
|
-
|
|
48
|
-
function captureStdout(fn: () => Promise<void>): Promise<string[]> {
|
|
49
|
-
const chunks: string[] = [];
|
|
50
|
-
const origWrite = process.stdout.write.bind(process.stdout);
|
|
51
|
-
process.stdout.write = ((chunk: unknown) => {
|
|
52
|
-
chunks.push(typeof chunk === "string" ? chunk : String(chunk));
|
|
53
|
-
return true;
|
|
54
|
-
}) as typeof process.stdout.write;
|
|
55
|
-
return fn()
|
|
56
|
-
.then(() => chunks)
|
|
57
|
-
.finally(() => {
|
|
58
|
-
process.stdout.write = origWrite;
|
|
59
|
-
});
|
|
60
|
-
}
|
|
61
|
-
|
|
62
|
-
// ---------------------------------------------------------------------------
|
|
63
|
-
// Tests
|
|
64
|
-
// ---------------------------------------------------------------------------
|
|
5
|
+
const ipc = setupPlatformIpcMock();
|
|
65
6
|
|
|
66
7
|
describe("assistant platform subscription", () => {
|
|
67
8
|
beforeEach(() => {
|
|
68
|
-
|
|
69
|
-
|
|
9
|
+
ipc.calls = [];
|
|
10
|
+
ipc.response = {
|
|
70
11
|
ok: true,
|
|
71
12
|
result: {
|
|
72
13
|
planId: "pro",
|
|
@@ -80,22 +21,12 @@ describe("assistant platform subscription", () => {
|
|
|
80
21
|
entitlements: { managedEmail: true, phoneNumber: false },
|
|
81
22
|
},
|
|
82
23
|
};
|
|
83
|
-
process.exitCode = 0;
|
|
84
24
|
});
|
|
85
25
|
|
|
86
26
|
test("calls platform_subscription and emits plan JSON with --json", async () => {
|
|
87
|
-
const out = await
|
|
88
|
-
const program = buildProgram();
|
|
89
|
-
await program.parseAsync([
|
|
90
|
-
"node",
|
|
91
|
-
"assistant",
|
|
92
|
-
"platform",
|
|
93
|
-
"subscription",
|
|
94
|
-
"--json",
|
|
95
|
-
]);
|
|
96
|
-
});
|
|
27
|
+
const out = await runPlatform(["subscription", "--json"]);
|
|
97
28
|
|
|
98
|
-
expect(
|
|
29
|
+
expect(ipc.calls[0][0]).toBe("platform_subscription");
|
|
99
30
|
|
|
100
31
|
const parsed = JSON.parse(out.join(""));
|
|
101
32
|
expect(parsed.planId).toBe("pro");
|
|
@@ -105,17 +36,9 @@ describe("assistant platform subscription", () => {
|
|
|
105
36
|
});
|
|
106
37
|
|
|
107
38
|
test("plain text mode does not emit JSON to stdout", async () => {
|
|
108
|
-
const out = await
|
|
109
|
-
const program = buildProgram();
|
|
110
|
-
await program.parseAsync([
|
|
111
|
-
"node",
|
|
112
|
-
"assistant",
|
|
113
|
-
"platform",
|
|
114
|
-
"subscription",
|
|
115
|
-
]);
|
|
116
|
-
});
|
|
39
|
+
const out = await runPlatform(["subscription"]);
|
|
117
40
|
|
|
118
|
-
// Plain-text mode logs via log.info
|
|
41
|
+
// Plain-text mode logs via log.info; verify writeOutput (JSON) was NOT called
|
|
119
42
|
expect(() => JSON.parse(out.join("").trim())).toThrow();
|
|
120
43
|
});
|
|
121
44
|
});
|
|
@@ -23,6 +23,7 @@ Examples:
|
|
|
23
23
|
$ assistant platform credits --json
|
|
24
24
|
$ assistant platform subscription --json
|
|
25
25
|
$ assistant platform plans --json
|
|
26
|
+
$ assistant platform invoices list --json
|
|
26
27
|
$ assistant platform connect
|
|
27
28
|
$ assistant platform disconnect
|
|
28
29
|
$ assistant platform callback-routes register --path webhooks/telegram --type telegram --json`,
|
|
@@ -145,6 +146,97 @@ Examples:
|
|
|
145
146
|
$ assistant platform plans
|
|
146
147
|
$ assistant platform plans --json`,
|
|
147
148
|
},
|
|
149
|
+
{
|
|
150
|
+
name: "invoices",
|
|
151
|
+
description: "Read the organization's Stripe invoice history",
|
|
152
|
+
helpText: `
|
|
153
|
+
Invoices are read from the platform's billing invoices endpoint using this
|
|
154
|
+
assistant's platform API key. Amounts are Stripe-scaled integers; see
|
|
155
|
+
'assistant platform invoices list --help' for the scaling rules. 'created'
|
|
156
|
+
is a Unix timestamp in seconds.
|
|
157
|
+
|
|
158
|
+
The platform returns one page of invoices at a time (newest first). When
|
|
159
|
+
the response's 'has_more' is true, pass the last invoice's id via
|
|
160
|
+
--starting-after to fetch the next (older) page.
|
|
161
|
+
|
|
162
|
+
Requires platform credentials (run 'assistant platform connect' first or
|
|
163
|
+
ensure VELLUM_PLATFORM_URL is set and credentials are stored).
|
|
164
|
+
|
|
165
|
+
Examples:
|
|
166
|
+
$ assistant platform invoices list --json
|
|
167
|
+
$ assistant platform invoices list --starting-after in_1AbCdEfGh
|
|
168
|
+
$ assistant platform invoices get in_1AbCdEfGh --json`,
|
|
169
|
+
subcommands: [
|
|
170
|
+
{
|
|
171
|
+
name: "list",
|
|
172
|
+
description:
|
|
173
|
+
"List one page of the organization's Stripe invoices, newest first",
|
|
174
|
+
options: [
|
|
175
|
+
{
|
|
176
|
+
flags: "--starting-after <invoice-id>",
|
|
177
|
+
description:
|
|
178
|
+
"Cursor: return invoices older than this invoice id (use the last id from the previous page)",
|
|
179
|
+
},
|
|
180
|
+
],
|
|
181
|
+
helpText: `
|
|
182
|
+
Fetches one page of the org's Stripe invoice history from the platform,
|
|
183
|
+
newest first.
|
|
184
|
+
|
|
185
|
+
Fields:
|
|
186
|
+
invoices One page of invoices, each with the fields below
|
|
187
|
+
id Stripe invoice ID; pass it to 'assistant platform
|
|
188
|
+
invoices get' or to --starting-after
|
|
189
|
+
number Human-readable invoice number, or null
|
|
190
|
+
status Stripe status (draft, open, paid, uncollectible,
|
|
191
|
+
void), or null
|
|
192
|
+
currency Lowercase ISO currency code (e.g. usd)
|
|
193
|
+
amount_due Amount due as a Stripe-scaled integer: divide by
|
|
194
|
+
100 for most currencies, by 1 for Stripe
|
|
195
|
+
zero-decimal currencies (e.g. JPY, KRW), and by
|
|
196
|
+
1000 for three-decimal ones (e.g. BHD); ISK, HUF,
|
|
197
|
+
TWD, and UGX are two-decimal for charges and
|
|
198
|
+
invoices (Stripe treats them as zero-decimal
|
|
199
|
+
only for payouts)
|
|
200
|
+
amount_paid Amount paid, same Stripe scaling as amount_due
|
|
201
|
+
amount_remaining Amount still owed, same Stripe scaling as amount_due
|
|
202
|
+
created Creation time as a Unix timestamp in seconds
|
|
203
|
+
hosted_invoice_url Link to the hosted Stripe invoice page, or null
|
|
204
|
+
invoice_pdf Link to the invoice PDF, or null
|
|
205
|
+
has_more True when older invoices exist beyond this page;
|
|
206
|
+
fetch them with --starting-after
|
|
207
|
+
|
|
208
|
+
Requires platform credentials (run 'assistant platform connect' first or
|
|
209
|
+
ensure VELLUM_PLATFORM_URL is set and credentials are stored).
|
|
210
|
+
|
|
211
|
+
Examples:
|
|
212
|
+
$ assistant platform invoices list --json
|
|
213
|
+
$ assistant platform invoices list --starting-after in_1AbCdEfGh --json`,
|
|
214
|
+
},
|
|
215
|
+
{
|
|
216
|
+
name: "get",
|
|
217
|
+
description: "Show a single Stripe invoice by ID",
|
|
218
|
+
arguments: [
|
|
219
|
+
{
|
|
220
|
+
name: "<invoice-id>",
|
|
221
|
+
description:
|
|
222
|
+
"Stripe invoice ID (e.g. in_1AbCdEfGh); run 'assistant platform invoices list' to find it",
|
|
223
|
+
},
|
|
224
|
+
],
|
|
225
|
+
helpText: `
|
|
226
|
+
The platform has no per-invoice endpoint, so the assistant pages through
|
|
227
|
+
the org's invoice list (newest first) until the ID matches. The lookup
|
|
228
|
+
searches the most recent 2,500 invoices (25 pages) and errors if the ID
|
|
229
|
+
is not found within that range.
|
|
230
|
+
|
|
231
|
+
Requires platform credentials (run 'assistant platform connect' first or
|
|
232
|
+
ensure VELLUM_PLATFORM_URL is set and credentials are stored).
|
|
233
|
+
|
|
234
|
+
Examples:
|
|
235
|
+
$ assistant platform invoices get in_1AbCdEfGh
|
|
236
|
+
$ assistant platform invoices get in_1AbCdEfGh --json`,
|
|
237
|
+
},
|
|
238
|
+
],
|
|
239
|
+
},
|
|
148
240
|
{
|
|
149
241
|
name: "disconnect",
|
|
150
242
|
description:
|
|
@@ -14,6 +14,7 @@ import { shouldOutputJson, writeOutput } from "../../output.js";
|
|
|
14
14
|
import { registerPlatformConnectCommand } from "./connect.js";
|
|
15
15
|
import { registerPlatformDisconnectCommand } from "./disconnect.js";
|
|
16
16
|
import { platformHelp } from "./index.help.js";
|
|
17
|
+
import { registerPlatformInvoicesCommands } from "./invoices.js";
|
|
17
18
|
|
|
18
19
|
interface PlatformStatusResult {
|
|
19
20
|
isPlatform: boolean;
|
|
@@ -236,6 +237,12 @@ export function registerPlatformCommand(program: Command): void {
|
|
|
236
237
|
},
|
|
237
238
|
);
|
|
238
239
|
|
|
240
|
+
// -----------------------------------------------------------------------
|
|
241
|
+
// invoices
|
|
242
|
+
// -----------------------------------------------------------------------
|
|
243
|
+
|
|
244
|
+
registerPlatformInvoicesCommands(platform);
|
|
245
|
+
|
|
239
246
|
// -----------------------------------------------------------------------
|
|
240
247
|
// disconnect
|
|
241
248
|
// -----------------------------------------------------------------------
|
|
@@ -0,0 +1,132 @@
|
|
|
1
|
+
import { stripeScaleDigits } from "@vellumai/service-contracts/stripe-currency";
|
|
2
|
+
import type { Command } from "commander";
|
|
3
|
+
|
|
4
|
+
import { cliIpcCall, exitFromIpcResult } from "../../../ipc/cli-client.js";
|
|
5
|
+
import { subcommand } from "../../lib/cli-command-help.js";
|
|
6
|
+
import { log } from "../../logger.js";
|
|
7
|
+
import { shouldOutputJson, writeOutput } from "../../output.js";
|
|
8
|
+
|
|
9
|
+
interface PlatformInvoice {
|
|
10
|
+
id: string;
|
|
11
|
+
number: string | null;
|
|
12
|
+
status: string | null;
|
|
13
|
+
currency: string;
|
|
14
|
+
amount_due: number;
|
|
15
|
+
amount_paid: number;
|
|
16
|
+
amount_remaining: number;
|
|
17
|
+
created: number;
|
|
18
|
+
hosted_invoice_url: string | null;
|
|
19
|
+
invoice_pdf: string | null;
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
interface PlatformInvoicesListResult {
|
|
23
|
+
invoices: PlatformInvoice[];
|
|
24
|
+
has_more: boolean;
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
/**
|
|
28
|
+
* Render a Stripe minor-unit amount as major units, e.g. "USD 12.34". The
|
|
29
|
+
* display fraction digits are forced to match stripeScaleDigits so Intl's
|
|
30
|
+
* ISO metadata cannot apply a different scale (see the currency sets in
|
|
31
|
+
* @vellumai/service-contracts/stripe-currency). Unknown currency codes fall
|
|
32
|
+
* back to the raw minor-unit amount.
|
|
33
|
+
*/
|
|
34
|
+
function formatInvoiceAmount(
|
|
35
|
+
amountMinorUnits: number,
|
|
36
|
+
currency: string,
|
|
37
|
+
): string {
|
|
38
|
+
const code = currency.toUpperCase();
|
|
39
|
+
const digits = stripeScaleDigits(code);
|
|
40
|
+
try {
|
|
41
|
+
const formatter = new Intl.NumberFormat("en-US", {
|
|
42
|
+
style: "currency",
|
|
43
|
+
currency: code,
|
|
44
|
+
currencyDisplay: "code",
|
|
45
|
+
minimumFractionDigits: digits,
|
|
46
|
+
maximumFractionDigits: digits,
|
|
47
|
+
});
|
|
48
|
+
// Intl separates the code and number with U+00A0; use a plain space.
|
|
49
|
+
return formatter
|
|
50
|
+
.format(amountMinorUnits / 10 ** digits)
|
|
51
|
+
.replace(/\u00a0/g, " ");
|
|
52
|
+
} catch {
|
|
53
|
+
// Intl.NumberFormat throws a RangeError on invalid currency codes.
|
|
54
|
+
return `${amountMinorUnits} ${code} (minor units)`;
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
function logInvoice(invoice: PlatformInvoice, includePdf: boolean): void {
|
|
59
|
+
log.info(invoice.number ?? invoice.id);
|
|
60
|
+
log.info(` Status: ${invoice.status ?? "unknown"}`);
|
|
61
|
+
log.info(
|
|
62
|
+
` Amount: ${formatInvoiceAmount(invoice.amount_due, invoice.currency)}`,
|
|
63
|
+
);
|
|
64
|
+
log.info(
|
|
65
|
+
` Created: ${new Date(invoice.created * 1000).toISOString().slice(0, 10)}`,
|
|
66
|
+
);
|
|
67
|
+
if (invoice.hosted_invoice_url) {
|
|
68
|
+
log.info(` URL: ${invoice.hosted_invoice_url}`);
|
|
69
|
+
}
|
|
70
|
+
if (includePdf && invoice.invoice_pdf) {
|
|
71
|
+
log.info(` PDF: ${invoice.invoice_pdf}`);
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
export function registerPlatformInvoicesCommands(platform: Command): void {
|
|
76
|
+
const invoices = subcommand(platform, "invoices");
|
|
77
|
+
|
|
78
|
+
subcommand(invoices, "list").action(
|
|
79
|
+
async (opts: { startingAfter?: string }, cmd: Command) => {
|
|
80
|
+
const r = await cliIpcCall<PlatformInvoicesListResult>(
|
|
81
|
+
"platform_invoices_list",
|
|
82
|
+
opts.startingAfter
|
|
83
|
+
? { queryParams: { starting_after: opts.startingAfter } }
|
|
84
|
+
: {},
|
|
85
|
+
);
|
|
86
|
+
if (!r.ok) {
|
|
87
|
+
return exitFromIpcResult(r, cmd);
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
const result = r.result!;
|
|
91
|
+
|
|
92
|
+
if (shouldOutputJson(cmd)) {
|
|
93
|
+
writeOutput(cmd, result);
|
|
94
|
+
} else {
|
|
95
|
+
if (result.invoices.length === 0) {
|
|
96
|
+
log.info("No invoices found.");
|
|
97
|
+
} else {
|
|
98
|
+
for (const invoice of result.invoices) {
|
|
99
|
+
logInvoice(invoice, false);
|
|
100
|
+
log.info("");
|
|
101
|
+
}
|
|
102
|
+
}
|
|
103
|
+
const lastId = result.invoices.at(-1)?.id;
|
|
104
|
+
if (result.has_more && lastId) {
|
|
105
|
+
log.info(
|
|
106
|
+
`More invoices available. Run 'assistant platform invoices list --starting-after ${lastId}' for the next page.`,
|
|
107
|
+
);
|
|
108
|
+
}
|
|
109
|
+
}
|
|
110
|
+
},
|
|
111
|
+
);
|
|
112
|
+
|
|
113
|
+
subcommand(invoices, "get").action(
|
|
114
|
+
async (invoiceId: string, _opts: Record<string, unknown>, cmd: Command) => {
|
|
115
|
+
const r = await cliIpcCall<PlatformInvoice>(
|
|
116
|
+
"platform_invoices_by_id_get",
|
|
117
|
+
{
|
|
118
|
+
pathParams: { id: invoiceId },
|
|
119
|
+
},
|
|
120
|
+
);
|
|
121
|
+
if (!r.ok) {
|
|
122
|
+
return exitFromIpcResult(r, cmd);
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
if (shouldOutputJson(cmd)) {
|
|
126
|
+
writeOutput(cmd, r.result!);
|
|
127
|
+
} else {
|
|
128
|
+
logInvoice(r.result!, true);
|
|
129
|
+
}
|
|
130
|
+
},
|
|
131
|
+
);
|
|
132
|
+
}
|
|
@@ -125,7 +125,7 @@ Grouping dimensions:
|
|
|
125
125
|
inference_profile Groups by inference profile; unset historical rows are
|
|
126
126
|
shown as Default / Unset
|
|
127
127
|
provider Groups by LLM provider (anthropic, openai, etc.)
|
|
128
|
-
model Groups by model name (claude-sonnet-
|
|
128
|
+
model Groups by model name (claude-sonnet-5, etc.)
|
|
129
129
|
conversation Groups by conversation ID
|
|
130
130
|
actor Legacy/internal subsystem grouping (main_agent, etc.)
|
|
131
131
|
|
|
@@ -14,6 +14,7 @@
|
|
|
14
14
|
|
|
15
15
|
import { existsSync, readdirSync, readFileSync, statSync } from "node:fs";
|
|
16
16
|
import { dirname, join } from "node:path";
|
|
17
|
+
import { fileURLToPath } from "node:url";
|
|
17
18
|
|
|
18
19
|
import { getWorkspacePluginsDir } from "../../util/platform.js";
|
|
19
20
|
import { parsePluginIcon } from "./plugin-artifact.js";
|
|
@@ -27,7 +28,7 @@ import { readValidatedPluginIcon } from "./plugin-icon-file.js";
|
|
|
27
28
|
* dependencies in test environments).
|
|
28
29
|
*/
|
|
29
30
|
const DEFAULT_PLUGINS_DIR = join(
|
|
30
|
-
dirname(
|
|
31
|
+
dirname(fileURLToPath(import.meta.url)),
|
|
31
32
|
"..",
|
|
32
33
|
"..",
|
|
33
34
|
"plugins",
|