@kosuke-ai/kosuke 0.0.0-stage → 1.0.1
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/CHANGELOG.md +62 -0
- package/README.md +199 -2
- package/dist/agent/http.js +18 -0
- package/dist/agent/summary.js +419 -0
- package/dist/commands/agent.js +22 -0
- package/dist/commands/auth.js +117 -0
- package/dist/commands/integrations.js +310 -0
- package/dist/commands/skill-runs.js +230 -0
- package/dist/commands/watch.js +167 -0
- package/dist/generated/commands/activity/list.js +73 -0
- package/dist/generated/commands/crons/artifacts-list.js +57 -0
- package/dist/generated/commands/crons/create.js +65 -0
- package/dist/generated/commands/crons/delete.js +18 -0
- package/dist/generated/commands/crons/get.js +19 -0
- package/dist/generated/commands/crons/list.js +54 -0
- package/dist/generated/commands/crons/projects-resume.js +21 -0
- package/dist/generated/commands/crons/run.js +37 -0
- package/dist/generated/commands/crons/runs-artifacts-get.js +23 -0
- package/dist/generated/commands/crons/runs-artifacts-list.js +22 -0
- package/dist/generated/commands/crons/runs-cancel.js +21 -0
- package/dist/generated/commands/crons/runs-diagnostics.js +20 -0
- package/dist/generated/commands/crons/runs-get.js +21 -0
- package/dist/generated/commands/crons/runs-list.js +58 -0
- package/dist/generated/commands/crons/runs-messages.js +21 -0
- package/dist/generated/commands/crons/update.js +73 -0
- package/dist/generated/commands/deploy/cancel.js +18 -0
- package/dist/generated/commands/deploy/list.js +33 -0
- package/dist/generated/commands/deploy/message.js +32 -0
- package/dist/generated/commands/deploy/messages.js +18 -0
- package/dist/generated/commands/deploy/queue-delete.js +19 -0
- package/dist/generated/commands/deploy/queue-list.js +19 -0
- package/dist/generated/commands/deploy/queue-move.js +31 -0
- package/dist/generated/commands/deploy/queue-resume.js +19 -0
- package/dist/generated/commands/deploy/queue-send.js +20 -0
- package/dist/generated/commands/deploy/queue-steer.js +20 -0
- package/dist/generated/commands/deploy/queue-update.js +56 -0
- package/dist/generated/commands/deploy/run.js +65 -0
- package/dist/generated/commands/deploy/set-model.js +28 -0
- package/dist/generated/commands/devices/list.js +18 -0
- package/dist/generated/commands/devices/request.js +31 -0
- package/dist/generated/commands/files/delete.js +18 -0
- package/dist/generated/commands/files/get.js +19 -0
- package/dist/generated/commands/files/list.js +25 -0
- package/dist/generated/commands/files/mkdir.js +28 -0
- package/dist/generated/commands/files/move.js +35 -0
- package/dist/generated/commands/files/put.js +92 -0
- package/dist/generated/commands/github/repositories.js +16 -0
- package/dist/generated/commands/github/status.js +16 -0
- package/dist/generated/commands/index.js +650 -0
- package/dist/generated/commands/integrations/execute.js +49 -0
- package/dist/generated/commands/integrations/tools.js +70 -0
- package/dist/generated/commands/mac/apps.js +30 -0
- package/dist/generated/commands/mac/ax-tree.js +65 -0
- package/dist/generated/commands/mac/browser-connection-check.js +69 -0
- package/dist/generated/commands/mac/browser-connections.js +30 -0
- package/dist/generated/commands/mac/browser-profiles.js +36 -0
- package/dist/generated/commands/mac/browser-sign-in.js +79 -0
- package/dist/generated/commands/mac/browser.js +44 -0
- package/dist/generated/commands/mac/click.js +72 -0
- package/dist/generated/commands/mac/file-delete.js +37 -0
- package/dist/generated/commands/mac/file-list.js +37 -0
- package/dist/generated/commands/mac/file-move.js +44 -0
- package/dist/generated/commands/mac/file-read.js +37 -0
- package/dist/generated/commands/mac/file-write.js +50 -0
- package/dist/generated/commands/mac/key.js +58 -0
- package/dist/generated/commands/mac/open.js +43 -0
- package/dist/generated/commands/mac/screenshot.js +51 -0
- package/dist/generated/commands/mac/scroll.js +70 -0
- package/dist/generated/commands/mac/shell.js +51 -0
- package/dist/generated/commands/mac/tools.js +18 -0
- package/dist/generated/commands/mac/type.js +64 -0
- package/dist/generated/commands/mac/windows.js +30 -0
- package/dist/generated/commands/members/list.js +24 -0
- package/dist/generated/commands/models/list.js +16 -0
- package/dist/generated/commands/personal-tasks/create.js +72 -0
- package/dist/generated/commands/personal-tasks/delete.js +17 -0
- package/dist/generated/commands/personal-tasks/get.js +20 -0
- package/dist/generated/commands/personal-tasks/list.js +71 -0
- package/dist/generated/commands/personal-tasks/move.js +31 -0
- package/dist/generated/commands/personal-tasks/start.js +69 -0
- package/dist/generated/commands/personal-tasks/update.js +81 -0
- package/dist/generated/commands/preview/get.js +26 -0
- package/dist/generated/commands/preview/health.js +26 -0
- package/dist/generated/commands/preview/reset.js +28 -0
- package/dist/generated/commands/preview/share-create.js +37 -0
- package/dist/generated/commands/preview/share-delete.js +26 -0
- package/dist/generated/commands/preview/share-get.js +27 -0
- package/dist/generated/commands/preview/shared-artifact.js +19 -0
- package/dist/generated/commands/preview/shared.js +17 -0
- package/dist/generated/commands/projects/backlog-items-create.js +35 -0
- package/dist/generated/commands/projects/backlog-items-delete.js +19 -0
- package/dist/generated/commands/projects/backlog-items-list.js +19 -0
- package/dist/generated/commands/projects/backlog-items-send.js +20 -0
- package/dist/generated/commands/projects/backlog-items-update.js +42 -0
- package/dist/generated/commands/projects/batch-merging-eject.js +37 -0
- package/dist/generated/commands/projects/batch-merging-finish.js +30 -0
- package/dist/generated/commands/projects/batch-merging-get.js +19 -0
- package/dist/generated/commands/projects/batch-merging-set.js +32 -0
- package/dist/generated/commands/projects/branches.js +31 -0
- package/dist/generated/commands/projects/create.js +72 -0
- package/dist/generated/commands/projects/delete.js +17 -0
- package/dist/generated/commands/projects/env-vars-add.js +22 -0
- package/dist/generated/commands/projects/env-vars-remove.js +21 -0
- package/dist/generated/commands/projects/get.js +18 -0
- package/dist/generated/commands/projects/labels-color.js +37 -0
- package/dist/generated/commands/projects/labels-delete.js +50 -0
- package/dist/generated/commands/projects/labels-usage.js +27 -0
- package/dist/generated/commands/projects/labels.js +18 -0
- package/dist/generated/commands/projects/list.js +16 -0
- package/dist/generated/commands/projects/live-logs.js +34 -0
- package/dist/generated/commands/projects/live-query.js +30 -0
- package/dist/generated/commands/projects/live-traffic.js +27 -0
- package/dist/generated/commands/projects/live-write-status.js +20 -0
- package/dist/generated/commands/projects/live-write.js +37 -0
- package/dist/generated/commands/projects/qa-guide-get.js +19 -0
- package/dist/generated/commands/projects/qa-guide-set.js +40 -0
- package/dist/generated/commands/projects/releases.js +18 -0
- package/dist/generated/commands/projects/services-add.js +88 -0
- package/dist/generated/commands/projects/services-list.js +19 -0
- package/dist/generated/commands/projects/services-remove.js +19 -0
- package/dist/generated/commands/projects/services-update.js +82 -0
- package/dist/generated/commands/projects/settings-get.js +19 -0
- package/dist/generated/commands/projects/settings-import.js +99 -0
- package/dist/generated/commands/projects/settings-update.js +85 -0
- package/dist/generated/commands/projects/skills.js +39 -0
- package/dist/generated/commands/projects/switch-workflow.js +34 -0
- package/dist/generated/commands/projects/update.js +28 -0
- package/dist/generated/commands/projects/versions.js +18 -0
- package/dist/generated/commands/sensei/cards-close.js +43 -0
- package/dist/generated/commands/sensei/cards-mark-read.js +30 -0
- package/dist/generated/commands/sensei/cards-put.js +152 -0
- package/dist/generated/commands/sensei/cards-send-reply.js +30 -0
- package/dist/generated/commands/sensei/cards.js +38 -0
- package/dist/generated/commands/sensei/chat.js +23 -0
- package/dist/generated/commands/sensei/connect.js +57 -0
- package/dist/generated/commands/sensei/delegates-get.js +21 -0
- package/dist/generated/commands/sensei/delegates-note.js +46 -0
- package/dist/generated/commands/sensei/delegates-start.js +51 -0
- package/dist/generated/commands/sensei/delegates-stop.js +21 -0
- package/dist/generated/commands/sensei/describe.js +36 -0
- package/dist/generated/commands/sensei/device-requests-decide.js +48 -0
- package/dist/generated/commands/sensei/device-requests-list.js +27 -0
- package/dist/generated/commands/sensei/memory-get.js +19 -0
- package/dist/generated/commands/sensei/memory-put.js +55 -0
- package/dist/generated/commands/sensei/merge-rule.js +36 -0
- package/dist/generated/commands/sensei/merge.js +36 -0
- package/dist/generated/commands/sensei/messages-reply.js +44 -0
- package/dist/generated/commands/sensei/messages-reroute.js +37 -0
- package/dist/generated/commands/sensei/messages-send.js +98 -0
- package/dist/generated/commands/sensei/notifications-open.js +19 -0
- package/dist/generated/commands/sensei/reels-activity.js +16 -0
- package/dist/generated/commands/sensei/reels-proposals.js +24 -0
- package/dist/generated/commands/sensei/reels-ranks.js +16 -0
- package/dist/generated/commands/sensei/reels-skips.js +32 -0
- package/dist/generated/commands/sensei/repository.js +36 -0
- package/dist/generated/commands/sensei/review-approve.js +37 -0
- package/dist/generated/commands/sensei/review-ask.js +44 -0
- package/dist/generated/commands/sensei/review-context.js +32 -0
- package/dist/generated/commands/sensei/sessions-assess.js +71 -0
- package/dist/generated/commands/sensei/sessions-facts.js +26 -0
- package/dist/generated/commands/sensei/suggest.js +36 -0
- package/dist/generated/commands/sensei/thread-done.js +21 -0
- package/dist/generated/commands/sensei/thread-move.js +30 -0
- package/dist/generated/commands/sensei/thread-read.js +41 -0
- package/dist/generated/commands/sensei/thread-split.js +30 -0
- package/dist/generated/commands/sensei/threads.js +47 -0
- package/dist/generated/commands/sensei/tickets-merge.js +51 -0
- package/dist/generated/commands/sensei/widgets.js +15 -0
- package/dist/generated/commands/sessions/activate.js +33 -0
- package/dist/generated/commands/sessions/all.js +142 -0
- package/dist/generated/commands/sessions/app-stack-start.js +20 -0
- package/dist/generated/commands/sessions/artifacts-attach.js +31 -0
- package/dist/generated/commands/sessions/artifacts-get.js +21 -0
- package/dist/generated/commands/sessions/artifacts-list.js +20 -0
- package/dist/generated/commands/sessions/create.js +234 -0
- package/dist/generated/commands/sessions/delete.js +21 -0
- package/dist/generated/commands/sessions/desktop-control.js +35 -0
- package/dist/generated/commands/sessions/desktop-start.js +19 -0
- package/dist/generated/commands/sessions/desktop.js +19 -0
- package/dist/generated/commands/sessions/diff-comments-add.js +65 -0
- package/dist/generated/commands/sessions/diff-comments-delete.js +21 -0
- package/dist/generated/commands/sessions/diff-comments-list.js +21 -0
- package/dist/generated/commands/sessions/diff-comments-update.js +25 -0
- package/dist/generated/commands/sessions/diff-commits.js +20 -0
- package/dist/generated/commands/sessions/diff-files.js +20 -0
- package/dist/generated/commands/sessions/diff-patch.js +87 -0
- package/dist/generated/commands/sessions/edit-prompt.js +81 -0
- package/dist/generated/commands/sessions/files-get.js +28 -0
- package/dist/generated/commands/sessions/files-list.js +20 -0
- package/dist/generated/commands/sessions/files-write.js +42 -0
- package/dist/generated/commands/sessions/get.js +19 -0
- package/dist/generated/commands/sessions/git-commit-message.js +20 -0
- package/dist/generated/commands/sessions/git-commit.js +30 -0
- package/dist/generated/commands/sessions/git-push.js +20 -0
- package/dist/generated/commands/sessions/git-state.js +20 -0
- package/dist/generated/commands/sessions/jobs-cancel.js +21 -0
- package/dist/generated/commands/sessions/jobs-get.js +21 -0
- package/dist/generated/commands/sessions/jobs-list.js +35 -0
- package/dist/generated/commands/sessions/labels-edit.js +45 -0
- package/dist/generated/commands/sessions/labels-set.js +45 -0
- package/dist/generated/commands/sessions/list.js +130 -0
- package/dist/generated/commands/sessions/mark-read.js +19 -0
- package/dist/generated/commands/sessions/message.js +33 -0
- package/dist/generated/commands/sessions/messages.js +41 -0
- package/dist/generated/commands/sessions/needs-you.js +16 -0
- package/dist/generated/commands/sessions/qa-clear.js +22 -0
- package/dist/generated/commands/sessions/qa-set.js +33 -0
- package/dist/generated/commands/sessions/queue-delete.js +23 -0
- package/dist/generated/commands/sessions/queue-list.js +20 -0
- package/dist/generated/commands/sessions/queue-move.js +32 -0
- package/dist/generated/commands/sessions/queue-resume.js +20 -0
- package/dist/generated/commands/sessions/queue-send.js +21 -0
- package/dist/generated/commands/sessions/queue-steer.js +21 -0
- package/dist/generated/commands/sessions/queue-update.js +57 -0
- package/dist/generated/commands/sessions/runtime-command.js +39 -0
- package/dist/generated/commands/sessions/search.js +24 -0
- package/dist/generated/commands/sessions/send.js +138 -0
- package/dist/generated/commands/sessions/steer.js +36 -0
- package/dist/generated/commands/sessions/sync.js +37 -0
- package/dist/generated/commands/sessions/title-generate.js +29 -0
- package/dist/generated/commands/sessions/update.js +118 -0
- package/dist/generated/commands/tasks/all.js +112 -0
- package/dist/generated/commands/tasks/create.js +230 -0
- package/dist/generated/commands/tasks/delete.js +18 -0
- package/dist/generated/commands/tasks/get.js +19 -0
- package/dist/generated/commands/tasks/labels.js +18 -0
- package/dist/generated/commands/tasks/list.js +114 -0
- package/dist/generated/commands/tasks/start.js +68 -0
- package/dist/generated/commands/tasks/update.js +136 -0
- package/dist/generated/commands/user/auth-token-revoke.js +16 -0
- package/dist/generated/commands/user/crons-artifacts-list.js +59 -0
- package/dist/generated/commands/user/crons-create.js +65 -0
- package/dist/generated/commands/user/crons-get.js +21 -0
- package/dist/generated/commands/user/crons-list.js +41 -0
- package/dist/generated/commands/user/crons-run.js +21 -0
- package/dist/generated/commands/user/crons-runs-artifacts-get.js +25 -0
- package/dist/generated/commands/user/crons-runs-artifacts-list.js +24 -0
- package/dist/generated/commands/user/crons-runs-get.js +23 -0
- package/dist/generated/commands/user/crons-runs-list.js +52 -0
- package/dist/generated/commands/user/crons-update.js +73 -0
- package/dist/generated/commands/user/files-delete.js +18 -0
- package/dist/generated/commands/user/files-get.js +19 -0
- package/dist/generated/commands/user/files-list.js +24 -0
- package/dist/generated/commands/user/files-mkdir.js +27 -0
- package/dist/generated/commands/user/files-move.js +35 -0
- package/dist/generated/commands/user/files-put.js +91 -0
- package/dist/generated/commands/user/integrations-linkedin-connect.js +35 -0
- package/dist/generated/commands/user/integrations-linkedin-list.js +17 -0
- package/dist/generated/commands/user/integrations-linkedin-reconnect.js +19 -0
- package/dist/generated/commands/user/integrations-list.js +16 -0
- package/dist/generated/commands/user/integrations-remove.js +19 -0
- package/dist/generated/commands/user/integrations-x-connect.js +35 -0
- package/dist/generated/commands/user/integrations-x-list.js +17 -0
- package/dist/generated/commands/user/integrations-x-reconnect.js +19 -0
- package/dist/generated/commands/user/memory-list.js +16 -0
- package/dist/generated/commands/user/memory-put.js +36 -0
- package/dist/generated/commands/user/plugins-list.js +28 -0
- package/dist/generated/commands/user/plugins-update.js +29 -0
- package/dist/generated/commands/user/profile.js +16 -0
- package/dist/generated/commands/user/push-devices-register.js +76 -0
- package/dist/generated/commands/user/push-devices-remove.js +26 -0
- package/dist/generated/commands/user/skills-create.js +33 -0
- package/dist/generated/commands/user/skills-delete.js +18 -0
- package/dist/generated/commands/user/skills-get.js +19 -0
- package/dist/generated/commands/user/skills-list.js +17 -0
- package/dist/generated/commands/user/skills-update.js +35 -0
- package/dist/generated/commands/user/subscriptions-delete.js +18 -0
- package/dist/generated/commands/user/subscriptions-get.js +17 -0
- package/dist/generated/commands/user/subscriptions-grok-change.js +20 -0
- package/dist/generated/commands/user/subscriptions-grok-get.js +18 -0
- package/dist/generated/commands/user/subscriptions-login-antigravity-cancel.js +34 -0
- package/dist/generated/commands/user/subscriptions-login-antigravity-complete.js +40 -0
- package/dist/generated/commands/user/subscriptions-login-antigravity-start.js +28 -0
- package/dist/generated/commands/user/subscriptions-login-claude-complete.js +34 -0
- package/dist/generated/commands/user/subscriptions-login-claude-start.js +29 -0
- package/dist/generated/commands/user/subscriptions-login-codex-complete.js +28 -0
- package/dist/generated/commands/user/subscriptions-login-codex-start.js +29 -0
- package/dist/generated/commands/user/subscriptions-login-muse-complete.js +28 -0
- package/dist/generated/commands/user/subscriptions-login-muse-start.js +29 -0
- package/dist/generated/commands/user/subscriptions-logins-delete.js +20 -0
- package/dist/generated/commands/user/subscriptions-logins-recheck.js +19 -0
- package/dist/generated/commands/user/subscriptions-set-default-model.js +33 -0
- package/dist/generated/commands/user/subscriptions-set-key.js +35 -0
- package/dist/generated/commands/user/subscriptions-token-usage.js +17 -0
- package/dist/generated/commands/user/subscriptions-usage.js +52 -0
- package/dist/generated/commands/workspaces/access-status.js +30 -0
- package/dist/generated/commands/workspaces/ai-provider-get.js +19 -0
- package/dist/generated/commands/workspaces/ai-provider-google-delete-method.js +33 -0
- package/dist/generated/commands/workspaces/ai-provider-google-get.js +20 -0
- package/dist/generated/commands/workspaces/ai-provider-google-remove.js +19 -0
- package/dist/generated/commands/workspaces/ai-provider-google-save.js +38 -0
- package/dist/generated/commands/workspaces/ai-provider-google-select-method.js +39 -0
- package/dist/generated/commands/workspaces/ai-provider-google-usage.js +39 -0
- package/dist/generated/commands/workspaces/ai-provider-login-antigravity-cancel.js +36 -0
- package/dist/generated/commands/workspaces/ai-provider-login-antigravity-complete.js +42 -0
- package/dist/generated/commands/workspaces/ai-provider-login-antigravity-start.js +30 -0
- package/dist/generated/commands/workspaces/ai-provider-logins-recheck.js +41 -0
- package/dist/generated/commands/workspaces/ai-provider-remove.js +57 -0
- package/dist/generated/commands/workspaces/ai-provider-set.js +21 -0
- package/dist/generated/commands/workspaces/ai-provider-token-usage.js +19 -0
- package/dist/generated/commands/workspaces/ai-provider-usage-claude.js +39 -0
- package/dist/generated/commands/workspaces/ai-provider-usage-codex.js +39 -0
- package/dist/generated/commands/workspaces/ai-provider-usage-coding-plan.js +32 -0
- package/dist/generated/commands/workspaces/ai-provider-usage-deepseek.js +32 -0
- package/dist/generated/commands/workspaces/ai-provider-usage-grok.js +39 -0
- package/dist/generated/commands/workspaces/ai-provider-usage-kimi-code.js +32 -0
- package/dist/generated/commands/workspaces/ai-provider-usage-muse.js +39 -0
- package/dist/generated/commands/workspaces/artifacts.js +26 -0
- package/dist/generated/commands/workspaces/credits.js +18 -0
- package/dist/generated/commands/workspaces/get.js +18 -0
- package/dist/generated/commands/workspaces/grok-change.js +21 -0
- package/dist/generated/commands/workspaces/grok-get.js +19 -0
- package/dist/generated/commands/workspaces/integrations-get.js +20 -0
- package/dist/generated/commands/workspaces/integrations-list.js +37 -0
- package/dist/generated/commands/workspaces/integrations-remove.js +19 -0
- package/dist/generated/commands/workspaces/list.js +16 -0
- package/dist/generated/commands/workspaces/members.js +26 -0
- package/dist/generated/commands/workspaces/plugins-list.js +30 -0
- package/dist/generated/commands/workspaces/plugins-update.js +30 -0
- package/dist/generated/commands/workspaces/provider-login-claude-complete.js +35 -0
- package/dist/generated/commands/workspaces/provider-login-claude-start.js +30 -0
- package/dist/generated/commands/workspaces/provider-login-codex-complete.js +29 -0
- package/dist/generated/commands/workspaces/provider-login-codex-start.js +30 -0
- package/dist/generated/commands/workspaces/provider-login-muse-complete.js +29 -0
- package/dist/generated/commands/workspaces/provider-login-muse-start.js +30 -0
- package/dist/generated/commands/workspaces/sandbox-limit.js +18 -0
- package/dist/generated/commands/workspaces/skills-create.js +35 -0
- package/dist/generated/commands/workspaces/skills-delete.js +19 -0
- package/dist/generated/commands/workspaces/skills-get.js +20 -0
- package/dist/generated/commands/workspaces/skills-list.js +19 -0
- package/dist/generated/commands/workspaces/skills-update.js +36 -0
- package/dist/generated/commands/workspaces/stop-sandboxes.js +28 -0
- package/dist/generated/commands/workspaces/timezone-set.js +29 -0
- package/dist/generated/commands/workspaces/usage.js +33 -0
- package/dist/generated/providers.js +31 -0
- package/dist/index.js +7 -0
- package/dist/integrations/api.js +136 -0
- package/dist/integrations/render.js +59 -0
- package/dist/program.js +73 -0
- package/dist/runtime/agent-tokens.js +39 -0
- package/dist/runtime/attach.js +199 -0
- package/dist/runtime/attachments.js +63 -0
- package/dist/runtime/client.js +188 -0
- package/dist/runtime/config.js +49 -0
- package/dist/runtime/credentials.js +32 -0
- package/dist/runtime/descriptor.js +7 -0
- package/dist/runtime/invoke.js +152 -0
- package/dist/runtime/job-watch.js +374 -0
- package/dist/runtime/runtime.js +46 -0
- package/dist/runtime/version.js +5 -0
- package/openapi.json +26091 -0
- package/package.json +104 -4
|
@@ -0,0 +1,419 @@
|
|
|
1
|
+
import { providers } from '../generated/providers.js';
|
|
2
|
+
import { readOverview } from '../integrations/api.js';
|
|
3
|
+
import { createApiClient } from '../runtime/client.js';
|
|
4
|
+
import { isTurnToken } from '../runtime/credentials.js';
|
|
5
|
+
import { HttpError, NEEDS_YOU_FILTER, paginationTotal } from './http.js';
|
|
6
|
+
/**
|
|
7
|
+
* `kosuke agent summary`: what needs the member, which providers and integrations
|
|
8
|
+
* are connected and what is New, in a few dozen lines an agent reads at the start of a
|
|
9
|
+
* conversation. Kept to about 1,500 tokens: each list is capped and names the
|
|
10
|
+
* command that reads the rest, and it claims only as many events as the other
|
|
11
|
+
* lines leave room for.
|
|
12
|
+
*/
|
|
13
|
+
const SUMMARY_TIMEOUT_MS = 8000;
|
|
14
|
+
/**
|
|
15
|
+
* Shorter than the summary's own: a usage read asks the provider, and a slow provider
|
|
16
|
+
* must cost the digest its numbers, never the digest.
|
|
17
|
+
*/
|
|
18
|
+
const PROVIDERS_TIMEOUT_MS = 4000;
|
|
19
|
+
/**
|
|
20
|
+
* Shorter still than the providers': connections are one cheap read, and a slow
|
|
21
|
+
* platform must cost the digest this section, never the digest.
|
|
22
|
+
*/
|
|
23
|
+
const INTEGRATIONS_TIMEOUT_MS = 3000;
|
|
24
|
+
const NEEDS_YOU_SHOWN = 5;
|
|
25
|
+
/** Connections listed before "and N more"; a member's own seven fit. */
|
|
26
|
+
const INTEGRATIONS_SHOWN = 8;
|
|
27
|
+
/** However much room is left: past this, the digest stops being a glance. */
|
|
28
|
+
const EVENTS_SHOWN = 15;
|
|
29
|
+
/**
|
|
30
|
+
* About 1,500 tokens, counted as characters at three a token: a URL's two
|
|
31
|
+
* UUIDs cost more tokens than words do, so a digest half links needs the margin.
|
|
32
|
+
*/
|
|
33
|
+
const BUDGET_CHARS = 4500;
|
|
34
|
+
/** The Activity section's heading, its "and N more" line, and the line breaks around them. */
|
|
35
|
+
const ACTIVITY_FRAME_CHARS = 150;
|
|
36
|
+
// Caps on the words, never on the URL: a clipped URL opens nothing, and a
|
|
37
|
+
// preview origin alone can take most of a line.
|
|
38
|
+
const TITLE_MAX = 90;
|
|
39
|
+
const TEXT_MAX = 140;
|
|
40
|
+
const NAME_MAX = 40;
|
|
41
|
+
/** `/projects/<uuid>/chat-sessions?session=<uuid>`, or a Backlog ticket's path, which is longer. */
|
|
42
|
+
const SESSION_PATH_CHARS = 120;
|
|
43
|
+
/** Room for an origin longer than any the digest has seen, such as a preview's. */
|
|
44
|
+
const ORIGIN_CHARS_MIN = 64;
|
|
45
|
+
// `satisfies`, so a query key the schema renames fails here, not at run time.
|
|
46
|
+
const NEEDS_YOU_QUERY = {
|
|
47
|
+
...NEEDS_YOU_FILTER,
|
|
48
|
+
limit: NEEDS_YOU_SHOWN,
|
|
49
|
+
};
|
|
50
|
+
const str = (value) => typeof value === 'string' && value.trim() ? value.trim() : null;
|
|
51
|
+
function rows(body) {
|
|
52
|
+
const list = body?.data;
|
|
53
|
+
return Array.isArray(list) ? list.filter((row) => row && typeof row === 'object') : [];
|
|
54
|
+
}
|
|
55
|
+
function where(row) {
|
|
56
|
+
const workspace = clip(str(row.workspaceName) ?? 'unnamed workspace', NAME_MAX);
|
|
57
|
+
const project = str(row.projectName);
|
|
58
|
+
return project ? `[${workspace}/${clip(project, NAME_MAX)}]` : `[${workspace}]`;
|
|
59
|
+
}
|
|
60
|
+
function clip(text, max) {
|
|
61
|
+
return text.length <= max ? text : text.slice(0, Math.max(0, max - 1)).trimEnd() + '…';
|
|
62
|
+
}
|
|
63
|
+
/**
|
|
64
|
+
* The longest an event line can be, so the digest claims only events it has
|
|
65
|
+
* room to print whole. Its session URL starts with the app's origin, which
|
|
66
|
+
* the digest knows only from the API's and the Needs You links.
|
|
67
|
+
*/
|
|
68
|
+
function eventLineMax(apiUrl, sessions) {
|
|
69
|
+
const origins = [apiUrl, ...sessions.map((row) => str(row.url))].flatMap((url) => {
|
|
70
|
+
try {
|
|
71
|
+
return url ? [new URL(url).origin.length] : [];
|
|
72
|
+
}
|
|
73
|
+
catch {
|
|
74
|
+
return [];
|
|
75
|
+
}
|
|
76
|
+
});
|
|
77
|
+
const url = Math.max(ORIGIN_CHARS_MIN, ...origins) + SESSION_PATH_CHARS;
|
|
78
|
+
return '- [/] '.length + 2 * NAME_MAX + TEXT_MAX + ', '.length + url;
|
|
79
|
+
}
|
|
80
|
+
/** `[workspace/project] title, reason, url`, one line per session. */
|
|
81
|
+
function sessionLine(row) {
|
|
82
|
+
const title = clip(str(row.title) ?? 'Untitled session', TITLE_MAX);
|
|
83
|
+
const reason = str(row.needsYouReason) ?? 'needs you';
|
|
84
|
+
return [`- ${where(row)} ${title}`, reason, str(row.url)].filter(Boolean).join(', ');
|
|
85
|
+
}
|
|
86
|
+
/** `[workspace/project] text: subject, url`, the same words Settings → Activity shows. */
|
|
87
|
+
function eventLine(event) {
|
|
88
|
+
const text = str(event.text) ?? str(event.kind) ?? 'Event';
|
|
89
|
+
const subject = str(event.subject);
|
|
90
|
+
const words = clip(`${text}${subject ? `: ${subject}` : ''}`, TEXT_MAX);
|
|
91
|
+
return [`- ${where(event)} ${words}`, str(event.sessionUrl)].filter(Boolean).join(', ');
|
|
92
|
+
}
|
|
93
|
+
const obj = (value) => value && typeof value === 'object' && !Array.isArray(value) ? value : null;
|
|
94
|
+
/** The `data` of a success envelope, or null when the call failed or answered something else. */
|
|
95
|
+
function dataOf(result) {
|
|
96
|
+
return result.response.ok ? obj(obj(result.data)?.data) : null;
|
|
97
|
+
}
|
|
98
|
+
/**
|
|
99
|
+
* A DeepSeek API key's balance, which it reports instead of windows: `balance 12.40 USD`,
|
|
100
|
+
* each currency apart, or `balance empty` once it cannot pay for a request.
|
|
101
|
+
*/
|
|
102
|
+
function balanceWords(usage) {
|
|
103
|
+
const balance = obj(usage?.balance);
|
|
104
|
+
if (!balance)
|
|
105
|
+
return null;
|
|
106
|
+
if (balance.available === false)
|
|
107
|
+
return 'balance empty';
|
|
108
|
+
const amounts = (Array.isArray(balance.amounts) ? balance.amounts : []).flatMap((amount) => {
|
|
109
|
+
const total = str(obj(amount)?.total);
|
|
110
|
+
const currency = str(obj(amount)?.currency);
|
|
111
|
+
return total && currency ? [`${total} ${currency}`] : [];
|
|
112
|
+
});
|
|
113
|
+
return amounts.length ? `balance ${amounts.join(', ')}` : null;
|
|
114
|
+
}
|
|
115
|
+
/** `session 42%, week 61%` or `session 42%, month 12%`, with only the windows the provider reported. */
|
|
116
|
+
function usageWords(usage) {
|
|
117
|
+
const windows = ['session', 'week', 'month'].flatMap((name) => {
|
|
118
|
+
const utilization = obj(usage?.[name])?.utilization;
|
|
119
|
+
return typeof utilization === 'number' && Number.isFinite(utilization)
|
|
120
|
+
? [`${name} ${Math.round(utilization)}% used`]
|
|
121
|
+
: [];
|
|
122
|
+
});
|
|
123
|
+
const balance = balanceWords(usage);
|
|
124
|
+
const words = balance ? [...windows, balance] : windows;
|
|
125
|
+
return words.length ? words.join(', ') : null;
|
|
126
|
+
}
|
|
127
|
+
const SUBSCRIPTIONS = {
|
|
128
|
+
anthropic: { label: 'Your Claude subscription', usage: 'claude_code' },
|
|
129
|
+
openai: { label: 'Your Codex subscription', usage: 'codex' },
|
|
130
|
+
zai: { label: 'Your GLM Coding Plan', usage: 'coding_plan' },
|
|
131
|
+
moonshot: { label: 'Your Kimi Code', usage: 'kimi_code' },
|
|
132
|
+
deepseek: { label: 'Your DeepSeek API key', usage: 'deepseek_api' },
|
|
133
|
+
meta: { label: 'Your Muse Code subscription', usage: 'muse_code' },
|
|
134
|
+
google: { label: 'Your Antigravity subscription', usage: 'antigravity' },
|
|
135
|
+
};
|
|
136
|
+
const AUTH_METHOD_NAMES = {
|
|
137
|
+
api_key: 'API key',
|
|
138
|
+
aws_bedrock: 'AWS Bedrock',
|
|
139
|
+
claude_code: 'Claude login',
|
|
140
|
+
codex: 'Codex login',
|
|
141
|
+
coding_plan: 'GLM Coding Plan',
|
|
142
|
+
kimi_code: 'Kimi Code',
|
|
143
|
+
deepseek_api: 'DeepSeek API',
|
|
144
|
+
muse_code: 'Muse Code',
|
|
145
|
+
antigravity: 'Antigravity',
|
|
146
|
+
gemini_api: 'Gemini API',
|
|
147
|
+
};
|
|
148
|
+
const WORKSPACE_USAGE = {
|
|
149
|
+
claude_code: '/api/organizations/{workspaceId}/ai-provider/claude-usage',
|
|
150
|
+
codex: '/api/organizations/{workspaceId}/ai-provider/codex-usage',
|
|
151
|
+
coding_plan: '/api/organizations/{workspaceId}/ai-provider/coding-plan-usage',
|
|
152
|
+
kimi_code: '/api/organizations/{workspaceId}/ai-provider/kimi-code-usage',
|
|
153
|
+
deepseek_api: '/api/organizations/{workspaceId}/ai-provider/deepseek-usage',
|
|
154
|
+
muse_code: '/api/organizations/{workspaceId}/ai-provider/muse-usage',
|
|
155
|
+
antigravity: '/api/organizations/{workspaceId}/ai-provider/google/usage',
|
|
156
|
+
};
|
|
157
|
+
function usagePlan(method) {
|
|
158
|
+
return Object.hasOwn(WORKSPACE_USAGE, method) ? method : null;
|
|
159
|
+
}
|
|
160
|
+
function subscriptionEntries(subscriptions) {
|
|
161
|
+
return providers.flatMap(({ key }) => {
|
|
162
|
+
const { label, usage } = SUBSCRIPTIONS[key];
|
|
163
|
+
if (key === 'google') {
|
|
164
|
+
const methods = obj(subscriptions.googleAccess)?.methods;
|
|
165
|
+
const signIn = Array.isArray(methods)
|
|
166
|
+
? methods.map(obj).find((method) => method?.method === 'antigravity')
|
|
167
|
+
: null;
|
|
168
|
+
return signIn ? [{ label, needsReauth: signIn.needsReauth === true, usage }] : [];
|
|
169
|
+
}
|
|
170
|
+
const row = obj(subscriptions[key]);
|
|
171
|
+
if (!row || row.connected !== true)
|
|
172
|
+
return [];
|
|
173
|
+
return [{ label, needsReauth: row.needsReauth === true, usage }];
|
|
174
|
+
});
|
|
175
|
+
}
|
|
176
|
+
/** One entry per provider the workspace has an active, configured auth method for. */
|
|
177
|
+
function workspaceEntries(settings) {
|
|
178
|
+
const configured = obj(settings.providers) ?? {};
|
|
179
|
+
return providers.flatMap(({ key, name }) => {
|
|
180
|
+
const provider = obj(configured[key]);
|
|
181
|
+
const method = str(provider?.activeAuthMethod);
|
|
182
|
+
const state = method ? obj(obj(provider?.authMethods)?.[method]) : null;
|
|
183
|
+
if (!method || !state || state.configured !== true)
|
|
184
|
+
return [];
|
|
185
|
+
return [
|
|
186
|
+
{
|
|
187
|
+
label: `Workspace ${name} (${AUTH_METHOD_NAMES[method] ?? method})`,
|
|
188
|
+
needsReauth: state.needsReauth === true,
|
|
189
|
+
usage: usagePlan(method),
|
|
190
|
+
},
|
|
191
|
+
];
|
|
192
|
+
});
|
|
193
|
+
}
|
|
194
|
+
function providerLine(entry, usage) {
|
|
195
|
+
if (entry.needsReauth)
|
|
196
|
+
return `- ${entry.label}: needs reconnecting`;
|
|
197
|
+
return [`- ${entry.label}: connected`, usageWords(usage)].filter(Boolean).join(', ');
|
|
198
|
+
}
|
|
199
|
+
/**
|
|
200
|
+
* The Providers section's lines, or null when a connection read failed: half the
|
|
201
|
+
* connections would read as the other half not existing. A usage read that fails
|
|
202
|
+
* costs only that line's numbers.
|
|
203
|
+
*/
|
|
204
|
+
async function fetchProviders(client, workspace, signal) {
|
|
205
|
+
try {
|
|
206
|
+
const [subscriptions, settings] = await Promise.all([
|
|
207
|
+
client.GET('/api/user/subscriptions', { signal }).then(dataOf),
|
|
208
|
+
workspace
|
|
209
|
+
? client
|
|
210
|
+
.GET('/api/organizations/{workspaceId}/ai-provider', {
|
|
211
|
+
params: { path: { workspaceId: workspace } },
|
|
212
|
+
signal,
|
|
213
|
+
})
|
|
214
|
+
.then(dataOf)
|
|
215
|
+
: null,
|
|
216
|
+
]);
|
|
217
|
+
if (!subscriptions || (workspace && !settings))
|
|
218
|
+
return null;
|
|
219
|
+
const usage = (read) => read.then(dataOf).catch(() => null);
|
|
220
|
+
const mine = subscriptionEntries(subscriptions).map((entry) => ({
|
|
221
|
+
entry,
|
|
222
|
+
read: () => usage(client.GET('/api/user/subscriptions/usage', {
|
|
223
|
+
params: { query: { provider: entry.usage ?? 'claude_code' } },
|
|
224
|
+
signal,
|
|
225
|
+
})),
|
|
226
|
+
}));
|
|
227
|
+
const path = { workspaceId: workspace ?? '' };
|
|
228
|
+
const shared = (settings ? workspaceEntries(settings) : []).map((entry) => ({
|
|
229
|
+
entry,
|
|
230
|
+
read: () => usage(client.GET(WORKSPACE_USAGE[entry.usage ?? 'claude_code'], { params: { path }, signal })),
|
|
231
|
+
}));
|
|
232
|
+
const lines = await Promise.all([...mine, ...shared].map(async ({ entry, read }) => providerLine(entry, entry.usage && !entry.needsReauth ? await read() : null)));
|
|
233
|
+
return lines.length ? lines : ['- None connected.'];
|
|
234
|
+
}
|
|
235
|
+
catch {
|
|
236
|
+
return null;
|
|
237
|
+
}
|
|
238
|
+
}
|
|
239
|
+
/**
|
|
240
|
+
* The Integrations section's lines, one per connection: toolkit, alias, scope, label and the
|
|
241
|
+
* account it reaches. The platform decides which are the caller's (a member's personal ones come
|
|
242
|
+
* only where the credential may use them), so this lists what it was sent, each once. Null when
|
|
243
|
+
* there is no workspace to ask about; a refusal or a failed read still says so, because an
|
|
244
|
+
* absent section would read as "nothing is connected".
|
|
245
|
+
*/
|
|
246
|
+
async function fetchIntegrations(client, workspace, apiKey, signal) {
|
|
247
|
+
// A member's key names a workspace; a turn's token already is one.
|
|
248
|
+
if (!workspace && !isTurnToken(apiKey))
|
|
249
|
+
return null;
|
|
250
|
+
try {
|
|
251
|
+
const result = await readOverview(client, workspace ? { workspace } : {}, signal);
|
|
252
|
+
if (!result.ok) {
|
|
253
|
+
return [
|
|
254
|
+
result.status === 403
|
|
255
|
+
? '- Not available to this credential.'
|
|
256
|
+
: '- Could not be read. `kosuke integrations list` retries.',
|
|
257
|
+
];
|
|
258
|
+
}
|
|
259
|
+
const seen = new Set();
|
|
260
|
+
const lines = [];
|
|
261
|
+
for (const toolkit of result.overview.toolkits) {
|
|
262
|
+
for (const connection of toolkit.connections) {
|
|
263
|
+
if (seen.has(connection.id))
|
|
264
|
+
continue;
|
|
265
|
+
seen.add(connection.id);
|
|
266
|
+
lines.push(integrationLine(toolkit.toolkit, connection));
|
|
267
|
+
}
|
|
268
|
+
}
|
|
269
|
+
if (lines.length === 0)
|
|
270
|
+
return ['- None connected.'];
|
|
271
|
+
const total = Math.max(result.overview.connectionTotal, lines.length);
|
|
272
|
+
const shown = lines.slice(0, INTEGRATIONS_SHOWN);
|
|
273
|
+
if (total > shown.length) {
|
|
274
|
+
const scope = workspace ? ` --workspace ${workspace}` : '';
|
|
275
|
+
shown.push(`(and ${total - shown.length} more: kosuke integrations list${scope})`);
|
|
276
|
+
}
|
|
277
|
+
return shown;
|
|
278
|
+
}
|
|
279
|
+
catch {
|
|
280
|
+
return null;
|
|
281
|
+
}
|
|
282
|
+
}
|
|
283
|
+
function integrationLine(toolkit, connection) {
|
|
284
|
+
const label = clip(connection.label, NAME_MAX);
|
|
285
|
+
const identity = clip(connection.accountIdentity ?? 'account unknown', NAME_MAX);
|
|
286
|
+
return `- ${clip(toolkit, NAME_MAX)}: ${connection.alias}, ${connection.scope}, "${label}", ${identity}`;
|
|
287
|
+
}
|
|
288
|
+
/** Never clipped: a workspace missing here would read as one the digest did not cover. */
|
|
289
|
+
function coverageLine(sessions) {
|
|
290
|
+
const covered = sessions?.meta?.workspaces;
|
|
291
|
+
const names = Array.isArray(covered)
|
|
292
|
+
? covered.map((workspace) => str(workspace?.name)).filter((name) => name !== null)
|
|
293
|
+
: [];
|
|
294
|
+
return `Workspaces covered: ${names.length ? names.join(', ') : 'none'}.`;
|
|
295
|
+
}
|
|
296
|
+
export function planSummary(options) {
|
|
297
|
+
const { sessions, apiUrl, now, workspace, peek = false } = options;
|
|
298
|
+
const scope = workspace ? ` --workspace ${workspace}` : '';
|
|
299
|
+
const stamp = new Date(now).toISOString().slice(0, 16).replace('T', ' ') + ' UTC';
|
|
300
|
+
const head = [`Kosuke summary, ${stamp}, ${apiUrl}`, ''];
|
|
301
|
+
const needing = rows(sessions);
|
|
302
|
+
const needTotal = paginationTotal(sessions) ?? needing.length;
|
|
303
|
+
if (needTotal === 0) {
|
|
304
|
+
head.push('No session needs you.');
|
|
305
|
+
}
|
|
306
|
+
else {
|
|
307
|
+
head.push(`${needTotal} session${needTotal === 1 ? ' needs' : 's need'} you:`);
|
|
308
|
+
const shown = needing.slice(0, NEEDS_YOU_SHOWN);
|
|
309
|
+
head.push(...shown.map(sessionLine));
|
|
310
|
+
if (needTotal > shown.length) {
|
|
311
|
+
head.push(`(and ${needTotal - shown.length} more: kosuke sessions all --needs-you --owners me${scope})`);
|
|
312
|
+
}
|
|
313
|
+
}
|
|
314
|
+
head.push('');
|
|
315
|
+
if (options.providers?.length)
|
|
316
|
+
head.push('Providers:', ...options.providers, '');
|
|
317
|
+
if (options.integrations?.length) {
|
|
318
|
+
head.push('Integrations (`kosuke integrations list` shows their tools):', ...options.integrations, '');
|
|
319
|
+
}
|
|
320
|
+
const coverage = coverageLine(sessions);
|
|
321
|
+
const room = BUDGET_CHARS - head.join('\n').length - coverage.length - ACTIVITY_FRAME_CHARS - scope.length;
|
|
322
|
+
const lineMax = eventLineMax(apiUrl, needing);
|
|
323
|
+
const claim = Math.max(0, Math.min(EVENTS_SHOWN, Math.floor(room / (lineMax + 1))));
|
|
324
|
+
return {
|
|
325
|
+
head,
|
|
326
|
+
coverage,
|
|
327
|
+
claim,
|
|
328
|
+
peek,
|
|
329
|
+
activityQuery: {
|
|
330
|
+
...(claim > 0 && !peek ? { markSeen: true } : { unseen: true }),
|
|
331
|
+
limit: Math.max(claim, 1),
|
|
332
|
+
...(workspace ? { workspace } : {}),
|
|
333
|
+
},
|
|
334
|
+
scope,
|
|
335
|
+
};
|
|
336
|
+
}
|
|
337
|
+
/** The whole digest, once `activity` answered the plan's `activityQuery`. */
|
|
338
|
+
export function renderSummary(plan, activity) {
|
|
339
|
+
const lines = [...plan.head];
|
|
340
|
+
// Every event the read returned is printed: without a peek, each is already marked seen.
|
|
341
|
+
const events = plan.claim > 0 ? rows(activity) : [];
|
|
342
|
+
const rest = (paginationTotal(activity) ?? events.length) - events.length;
|
|
343
|
+
const noun = (count) => (count === 1 ? 'event' : 'events');
|
|
344
|
+
if (events.length) {
|
|
345
|
+
const state = plan.peek ? 'still New' : 'now marked seen';
|
|
346
|
+
lines.push(`${events.length} New ${noun(events.length)}, oldest first, ${state}:`);
|
|
347
|
+
lines.push(...events.map(eventLine));
|
|
348
|
+
if (rest > 0) {
|
|
349
|
+
lines.push(`(and ${rest} more New ${noun(rest)}, still New: kosuke activity list --unseen${plan.scope})`);
|
|
350
|
+
}
|
|
351
|
+
}
|
|
352
|
+
else if (rest > 0) {
|
|
353
|
+
lines.push(`${rest} New ${noun(rest)}, not listed here and still New: kosuke activity list --unseen${plan.scope}`);
|
|
354
|
+
}
|
|
355
|
+
else {
|
|
356
|
+
lines.push('No new activity.');
|
|
357
|
+
}
|
|
358
|
+
lines.push('', plan.coverage);
|
|
359
|
+
return lines.join('\n');
|
|
360
|
+
}
|
|
361
|
+
/**
|
|
362
|
+
* The whole digest, or one line saying why not, so the agent reading it
|
|
363
|
+
* knows to query the CLI itself. Needs You, the providers and the integrations are read first:
|
|
364
|
+
* their lines decide how many events fit, and the Activity read marks events
|
|
365
|
+
* seen, so a digest that failed after it would lose them.
|
|
366
|
+
*/
|
|
367
|
+
export async function fetchSummary(options) {
|
|
368
|
+
if (!options.apiKey)
|
|
369
|
+
return unavailable('not logged in', 'kosuke auth login');
|
|
370
|
+
const client = createApiClient({
|
|
371
|
+
apiUrl: options.apiUrl,
|
|
372
|
+
apiKey: options.apiKey,
|
|
373
|
+
fetch: options.fetch,
|
|
374
|
+
});
|
|
375
|
+
const signal = AbortSignal.timeout(options.timeoutMs ?? SUMMARY_TIMEOUT_MS);
|
|
376
|
+
try {
|
|
377
|
+
const workspace = options.workspace ? { workspace: options.workspace } : {};
|
|
378
|
+
const [needsYou, providers, integrations] = await Promise.all([
|
|
379
|
+
client.GET('/api/chat-sessions', {
|
|
380
|
+
params: { query: { ...NEEDS_YOU_QUERY, ...workspace } },
|
|
381
|
+
signal,
|
|
382
|
+
}),
|
|
383
|
+
fetchProviders(client, options.workspace, AbortSignal.any([signal, AbortSignal.timeout(PROVIDERS_TIMEOUT_MS)])),
|
|
384
|
+
fetchIntegrations(client, options.workspace, options.apiKey, AbortSignal.any([signal, AbortSignal.timeout(INTEGRATIONS_TIMEOUT_MS)])),
|
|
385
|
+
]);
|
|
386
|
+
const sessions = body(needsYou);
|
|
387
|
+
const plan = planSummary({
|
|
388
|
+
sessions,
|
|
389
|
+
providers,
|
|
390
|
+
integrations,
|
|
391
|
+
apiUrl: options.apiUrl,
|
|
392
|
+
now: (options.now ?? Date.now)(),
|
|
393
|
+
...workspace,
|
|
394
|
+
peek: options.peek ?? false,
|
|
395
|
+
});
|
|
396
|
+
const activity = body(await client.GET('/api/activity', { params: { query: plan.activityQuery }, signal }));
|
|
397
|
+
return renderSummary(plan, activity);
|
|
398
|
+
}
|
|
399
|
+
catch (error) {
|
|
400
|
+
return unavailable(reasonOf(error), 'kosuke auth status');
|
|
401
|
+
}
|
|
402
|
+
}
|
|
403
|
+
function body(result) {
|
|
404
|
+
if (!result.response.ok)
|
|
405
|
+
throw new HttpError(result.response.status);
|
|
406
|
+
return result.data;
|
|
407
|
+
}
|
|
408
|
+
function reasonOf(error) {
|
|
409
|
+
if (error instanceof HttpError) {
|
|
410
|
+
return error.status === 401 ? 'the API key was refused (HTTP 401)' : `HTTP ${error.status}`;
|
|
411
|
+
}
|
|
412
|
+
const name = error instanceof Error ? error.name : '';
|
|
413
|
+
if (name === 'TimeoutError' || name === 'AbortError')
|
|
414
|
+
return 'the platform did not answer in time';
|
|
415
|
+
return 'the platform could not be reached';
|
|
416
|
+
}
|
|
417
|
+
function unavailable(reason, next) {
|
|
418
|
+
return `Kosuke summary unavailable: ${reason}. Run ${next}.`;
|
|
419
|
+
}
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
import { fetchSummary } from '../agent/summary.js';
|
|
2
|
+
export function attachAgentCommands(root, runtime, deps = {}) {
|
|
3
|
+
const { fetch, now, stdout = (line) => process.stdout.write(line + '\n') } = deps;
|
|
4
|
+
const agent = root.command('agent').description('What an agent reads about your Kosuke.');
|
|
5
|
+
agent
|
|
6
|
+
.command('summary')
|
|
7
|
+
.description('Print what needs you and your oldest New Activity as a short digest for an agent to read. ' +
|
|
8
|
+
'Marks seen only the events it prints, and none with --peek; the rest stay New.')
|
|
9
|
+
.option('--workspace <id>', 'Only this workspace.')
|
|
10
|
+
.option('--peek', 'Print the events without marking them seen.')
|
|
11
|
+
.action(async (options) => {
|
|
12
|
+
const { apiKey, apiUrl } = runtime.credentials();
|
|
13
|
+
stdout(await fetchSummary({
|
|
14
|
+
apiUrl,
|
|
15
|
+
apiKey,
|
|
16
|
+
...(options.workspace ? { workspace: options.workspace } : {}),
|
|
17
|
+
...(options.peek ? { peek: true } : {}),
|
|
18
|
+
...(fetch ? { fetch } : {}),
|
|
19
|
+
...(now ? { now } : {}),
|
|
20
|
+
}));
|
|
21
|
+
});
|
|
22
|
+
}
|
|
@@ -0,0 +1,117 @@
|
|
|
1
|
+
import { createApiClient } from '../runtime/client.js';
|
|
2
|
+
import { credentialsPath, DEFAULT_API_URL, deleteCredentials, writeCredentials, } from '../runtime/config.js';
|
|
3
|
+
export function attachAuthCommands(root, runtime, deps = {}) {
|
|
4
|
+
const { env = process.env, fetch, prompt = promptHidden, stdout = (line) => process.stdout.write(line + '\n'), stderr = (line) => process.stderr.write(line + '\n'), exit = (code) => {
|
|
5
|
+
process.exitCode = code;
|
|
6
|
+
}, } = deps;
|
|
7
|
+
const auth = root.command('auth').description('Manage the API key this machine uses.');
|
|
8
|
+
auth
|
|
9
|
+
.command('login')
|
|
10
|
+
.description('Store an API key, after checking it works. Takes the global --api-key when ' +
|
|
11
|
+
'given, and prompts otherwise. Create one in Settings.')
|
|
12
|
+
.action(async () => {
|
|
13
|
+
// The global --api-key is the one flag: commander reads a program option
|
|
14
|
+
// wherever it appears, so a second `--api-key` on this subcommand would
|
|
15
|
+
// never receive the value. Only that flag counts as the key to store:
|
|
16
|
+
// logging in is an explicit act, and a stale KOSUKE_API_KEY left in the
|
|
17
|
+
// shell is exactly what someone running this may be trying to replace.
|
|
18
|
+
const resolved = runtime.credentials();
|
|
19
|
+
const apiUrl = resolved.apiUrl;
|
|
20
|
+
const apiKey = resolved.apiKeySource === 'flag' ? resolved.apiKey : (await prompt('API key: ')).trim();
|
|
21
|
+
if (!apiKey) {
|
|
22
|
+
stderr('No API key given.');
|
|
23
|
+
exit(1);
|
|
24
|
+
return;
|
|
25
|
+
}
|
|
26
|
+
const who = await verify(apiUrl, apiKey, fetch);
|
|
27
|
+
if (!who.ok) {
|
|
28
|
+
stderr(`The API key was refused by ${apiUrl} (HTTP ${who.status}). Nothing saved.`);
|
|
29
|
+
exit(1);
|
|
30
|
+
return;
|
|
31
|
+
}
|
|
32
|
+
const path = writeCredentials({ apiKey, ...(apiUrl !== DEFAULT_API_URL ? { apiUrl } : {}) }, env);
|
|
33
|
+
stdout(`Logged in to ${apiUrl}; member of ${who.workspaces} workspace${who.workspaces === 1 ? '' : 's'}. API key saved to ${path}.`);
|
|
34
|
+
});
|
|
35
|
+
auth
|
|
36
|
+
.command('status')
|
|
37
|
+
.description('Show where the API key comes from and whether the platform accepts it.')
|
|
38
|
+
.action(async () => {
|
|
39
|
+
const { apiKey, apiKeySource, apiUrl, apiUrlSource } = runtime.credentials();
|
|
40
|
+
const result = {
|
|
41
|
+
apiUrl,
|
|
42
|
+
apiUrlSource,
|
|
43
|
+
apiKeySource,
|
|
44
|
+
credentialsFile: credentialsPath(env),
|
|
45
|
+
};
|
|
46
|
+
if (apiKey) {
|
|
47
|
+
const who = await verify(apiUrl, apiKey, fetch);
|
|
48
|
+
result.tokenPrefix = apiKey.slice(0, 12);
|
|
49
|
+
result.accepted = who.ok;
|
|
50
|
+
if (who.ok)
|
|
51
|
+
result.workspaces = who.workspaces;
|
|
52
|
+
else
|
|
53
|
+
result.status = who.status;
|
|
54
|
+
}
|
|
55
|
+
else {
|
|
56
|
+
result.accepted = false;
|
|
57
|
+
}
|
|
58
|
+
stdout(JSON.stringify(result, null, 2));
|
|
59
|
+
if (!result.accepted)
|
|
60
|
+
exit(1);
|
|
61
|
+
});
|
|
62
|
+
auth
|
|
63
|
+
.command('logout')
|
|
64
|
+
.description('Delete the stored API key. A key from the environment or a flag is untouched.')
|
|
65
|
+
.action(() => {
|
|
66
|
+
const path = credentialsPath(env);
|
|
67
|
+
stdout(deleteCredentials(env) ? `Removed ${path}.` : `Nothing stored at ${path}.`);
|
|
68
|
+
});
|
|
69
|
+
}
|
|
70
|
+
/** The cheapest call a key must be able to make: the workspaces it belongs to. */
|
|
71
|
+
async function verify(apiUrl, apiKey, fetch) {
|
|
72
|
+
const client = createApiClient({ apiUrl, apiKey, fetch });
|
|
73
|
+
const { data, response } = await client.GET('/api/organizations');
|
|
74
|
+
if (!response.ok)
|
|
75
|
+
return { ok: false, status: response.status };
|
|
76
|
+
const list = data?.data;
|
|
77
|
+
return { ok: true, workspaces: Array.isArray(list) ? list.length : 0 };
|
|
78
|
+
}
|
|
79
|
+
/** Read a line without echoing it, when stdin is a terminal. */
|
|
80
|
+
async function promptHidden(question) {
|
|
81
|
+
const { stdin, stdout } = process;
|
|
82
|
+
stdout.write(question);
|
|
83
|
+
if (!stdin.isTTY) {
|
|
84
|
+
const { createInterface } = await import('node:readline/promises');
|
|
85
|
+
const rl = createInterface({ input: stdin, terminal: false });
|
|
86
|
+
const answer = await rl.question('');
|
|
87
|
+
rl.close();
|
|
88
|
+
return answer;
|
|
89
|
+
}
|
|
90
|
+
return new Promise((resolve) => {
|
|
91
|
+
let answer = '';
|
|
92
|
+
stdin.setRawMode(true);
|
|
93
|
+
stdin.resume();
|
|
94
|
+
stdin.setEncoding('utf8');
|
|
95
|
+
const onData = (chunk) => {
|
|
96
|
+
for (const char of chunk) {
|
|
97
|
+
if (char === '\n' || char === '\r' || char === '\u0004') {
|
|
98
|
+
stdin.setRawMode(false);
|
|
99
|
+
stdin.pause();
|
|
100
|
+
stdin.off('data', onData);
|
|
101
|
+
stdout.write('\n');
|
|
102
|
+
resolve(answer);
|
|
103
|
+
return;
|
|
104
|
+
}
|
|
105
|
+
if (char === '\u0003') {
|
|
106
|
+
stdin.setRawMode(false);
|
|
107
|
+
process.exit(130);
|
|
108
|
+
}
|
|
109
|
+
if (char === '\u007f' || char === '\b')
|
|
110
|
+
answer = answer.slice(0, -1);
|
|
111
|
+
else
|
|
112
|
+
answer += char;
|
|
113
|
+
}
|
|
114
|
+
};
|
|
115
|
+
stdin.on('data', onData);
|
|
116
|
+
});
|
|
117
|
+
}
|