@aitne-sh/aitne 0.1.8 → 0.1.10
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +251 -164
- package/agent-assets/agent-profiles/_safety.md +3 -3
- package/agent-assets/agent-profiles/browser-task.md +108 -0
- package/agent-assets/agent-profiles/conversational.md +3 -3
- package/agent-assets/agent-profiles/profile-importer.md +2 -2
- package/agent-assets/agent-profiles/routine-fetch-window.md +30 -19
- package/agent-assets/agents/context-index-reconcile/agent.md +52 -0
- package/agent-assets/agents/evening-review/agent.md +53 -0
- package/agent-assets/agents/hourly-check/agent.md +62 -0
- package/agent-assets/agents/monthly-review/agent.md +55 -0
- package/agent-assets/agents/morning-routine/agent.md +78 -0
- package/agent-assets/agents/roadmap-maintenance/agent.md +52 -0
- package/agent-assets/agents/skill-curation/agent.md +52 -0
- package/agent-assets/agents/user-profile-sweep-evening/agent.md +48 -0
- package/agent-assets/agents/user-profile-sweep-morning/agent.md +53 -0
- package/agent-assets/agents/weekly-review/agent.md +51 -0
- package/agent-assets/docs/concepts/agent-day.md +13 -11
- package/agent-assets/docs/concepts/auth-health.md +47 -10
- package/agent-assets/docs/concepts/backends-and-tiers.md +66 -31
- package/agent-assets/docs/concepts/costs-and-quotas.md +51 -15
- package/agent-assets/docs/concepts/delegated-mode.md +56 -17
- package/agent-assets/docs/concepts/memory-model.md +77 -34
- package/agent-assets/docs/concepts/observations.md +49 -11
- package/agent-assets/docs/concepts/process-keys.md +56 -22
- package/agent-assets/docs/concepts/routines.md +60 -33
- package/agent-assets/docs/concepts/safety-and-execution.md +50 -21
- package/agent-assets/docs/concepts/safety-model.md +61 -50
- package/agent-assets/docs/concepts/skills.md +34 -18
- package/agent-assets/docs/features/integrations/browser-history.md +196 -0
- package/agent-assets/docs/features/integrations/calendar.md +39 -29
- package/agent-assets/docs/features/integrations/git.md +18 -7
- package/agent-assets/docs/features/integrations/github.md +84 -33
- package/agent-assets/docs/features/integrations/mail.md +61 -17
- package/agent-assets/docs/features/integrations/notion.md +18 -6
- package/agent-assets/docs/features/integrations/obsidian.md +28 -5
- package/agent-assets/docs/features/lifestyle/git.md +44 -40
- package/agent-assets/docs/features/lifestyle/reading.md +57 -22
- package/agent-assets/docs/features/lifestyle/receipts.md +51 -21
- package/agent-assets/docs/features/lifestyle/travel-bookings.md +77 -14
- package/agent-assets/docs/features/memory-files/agent-journal.md +132 -53
- package/agent-assets/docs/features/memory-files/agent-lessons.md +177 -0
- package/agent-assets/docs/features/memory-files/projects.md +73 -17
- package/agent-assets/docs/features/memory-files/roadmap.md +54 -11
- package/agent-assets/docs/features/memory-files/schedule.md +113 -70
- package/agent-assets/docs/features/memory-files/today.md +46 -21
- package/agent-assets/docs/features/memory-files/user-profile.md +63 -33
- package/agent-assets/docs/features/messaging/bang-commands.md +113 -36
- package/agent-assets/docs/features/messaging/dashboard-chat.md +43 -21
- package/agent-assets/docs/features/messaging/discord.md +35 -4
- package/agent-assets/docs/features/messaging/overview.md +37 -19
- package/agent-assets/docs/features/messaging/pairing-and-magic-phrase.md +94 -27
- package/agent-assets/docs/features/messaging/slack.md +67 -14
- package/agent-assets/docs/features/messaging/telegram.md +22 -8
- package/agent-assets/docs/features/messaging/whatsapp.md +71 -17
- package/agent-assets/docs/features/operations/activity-and-conversations.md +45 -15
- package/agent-assets/docs/features/operations/approvals.md +49 -16
- package/agent-assets/docs/features/operations/backend-routing.md +68 -16
- package/agent-assets/docs/features/operations/cost-tracking.md +84 -17
- package/agent-assets/docs/features/operations/managed-chromium.md +222 -0
- package/agent-assets/docs/features/operations/notifications.md +52 -11
- package/agent-assets/docs/features/operations/quiet-hours.md +64 -40
- package/agent-assets/docs/features/operations/schedule-approaching.md +54 -24
- package/agent-assets/docs/features/routines/custom-routines.md +98 -26
- package/agent-assets/docs/features/routines/evening-review.md +82 -21
- package/agent-assets/docs/features/routines/hourly-check.md +149 -29
- package/agent-assets/docs/features/routines/morning-routine.md +54 -35
- package/agent-assets/docs/features/routines/weekly-review.md +46 -21
- package/agent-assets/docs/features/wiki/commands.md +26 -16
- package/agent-assets/docs/features/wiki/cost-and-approval.md +241 -0
- package/agent-assets/docs/features/wiki/dashboard.md +256 -0
- package/agent-assets/docs/features/wiki/overview.md +70 -12
- package/agent-assets/docs/features/wiki/search.md +248 -0
- package/agent-assets/docs/features/wiki/workspaces.md +254 -0
- package/agent-assets/docs/getting-started/01-what-is-this.md +34 -23
- package/agent-assets/docs/getting-started/02-first-steps.md +17 -10
- package/agent-assets/docs/getting-started/03-what-can-this-do.md +25 -14
- package/agent-assets/docs/getting-started/04-first-day.md +39 -21
- package/agent-assets/docs/glossary.md +235 -24
- package/agent-assets/docs/guides/add-a-custom-routine.md +63 -23
- package/agent-assets/docs/guides/backup-and-restore.md +80 -16
- package/agent-assets/docs/guides/budget-and-cost-for-wiki.md +57 -26
- package/agent-assets/docs/guides/build-your-wiki.md +22 -9
- package/agent-assets/docs/guides/change-which-model-handles-x.md +64 -10
- package/agent-assets/docs/guides/connect-a-new-mail-account.md +66 -15
- package/agent-assets/docs/guides/explore-with-trace-and-connect.md +32 -14
- package/agent-assets/docs/guides/import-knowledge-file.md +50 -40
- package/agent-assets/docs/guides/install-and-run.md +49 -20
- package/agent-assets/docs/guides/maintain-wiki-health.md +35 -10
- package/agent-assets/docs/guides/migrate-machines.md +74 -18
- package/agent-assets/docs/guides/multiple-wikis-for-multiple-domains.md +111 -60
- package/agent-assets/docs/guides/pause-the-agent.md +69 -24
- package/agent-assets/docs/guides/reinstall-cleanly.md +88 -18
- package/agent-assets/docs/guides/setup-wizard.md +116 -54
- package/agent-assets/docs/guides/switch-default-backend.md +62 -16
- package/agent-assets/docs/guides/use-an-existing-obsidian-vault.md +30 -14
- package/agent-assets/docs/reference/api.md +153 -32
- package/agent-assets/docs/reference/cli-commands.md +39 -18
- package/agent-assets/docs/reference/config.md +241 -49
- package/agent-assets/docs/reference/disallowed-tools.md +34 -13
- package/agent-assets/docs/reference/keyboard-shortcuts.md +34 -10
- package/agent-assets/docs/reference/knowledge-layout.md +629 -0
- package/agent-assets/docs/reference/process-keys.md +62 -6
- package/agent-assets/docs/reference/skills.md +41 -14
- package/agent-assets/docs/troubleshooting/auth-failed.md +51 -21
- package/agent-assets/docs/troubleshooting/dashboard-shows-degraded.md +97 -28
- package/agent-assets/docs/troubleshooting/fallback-keeps-firing.md +86 -22
- package/agent-assets/docs/troubleshooting/messaging-not-pairing.md +68 -24
- package/agent-assets/docs/troubleshooting/morning-routine-didnt-run.md +80 -20
- package/agent-assets/docs/troubleshooting/observation-not-detected.md +73 -21
- package/agent-assets/docs/troubleshooting/quota-exhausted.md +33 -8
- package/agent-assets/docs/troubleshooting/wiki-ingest-full-blocked.md +126 -54
- package/agent-assets/docs/troubleshooting/wiki-write-failed.md +29 -12
- package/agent-assets/optimizer-skills/drift-analysis/SKILL.md +1 -1
- package/agent-assets/optimizer-skills/knowledge-map/SKILL.md +1 -1
- package/agent-assets/optimizer-skills/skill-curation/SKILL.md +1 -1
- package/agent-assets/sandbox/linux/aitne-chromium.apparmor +91 -0
- package/agent-assets/sandbox/macos/aitne-chromium.sb +156 -0
- package/agent-assets/skills/agent-actions/SKILL.md +25 -41
- package/agent-assets/skills/agent-create/SKILL.md +158 -0
- package/agent-assets/skills/attach/SKILL.md +10 -29
- package/agent-assets/skills/browser-history/SKILL.md +211 -0
- package/agent-assets/skills/browser-history-respond/SKILL.md +111 -0
- package/agent-assets/skills/browser-task/SKILL.md +164 -0
- package/agent-assets/skills/context/SKILL.md +35 -44
- package/agent-assets/skills/context/curation.json +14 -14
- package/agent-assets/skills/context/references/api.md +52 -40
- package/agent-assets/skills/context/references/required-frontmatter.md +13 -12
- package/agent-assets/skills/context/references/snapshot-files.md +18 -17
- package/agent-assets/skills/context/seeds/file-responsibilities.seed.json +8 -8
- package/agent-assets/skills/context/seeds/frontmatter-requirements.seed.json +3 -3
- package/agent-assets/skills/docs-search/SKILL.md +23 -34
- package/agent-assets/skills/external-services/SKILL.delegated.claude.md +17 -114
- package/agent-assets/skills/external-services/SKILL.delegated.codex.md +17 -113
- package/agent-assets/skills/external-services/SKILL.delegated.gemini.md +17 -113
- package/agent-assets/skills/external-services/SKILL.md +3 -3
- package/agent-assets/skills/external-services/SKILL.native.claude.md +7 -7
- package/agent-assets/skills/external-services/SKILL.native.codex.md +7 -7
- package/agent-assets/skills/external-services/SKILL.native.gemini.md +4 -4
- package/agent-assets/skills/external-services/references/calendar-apple.md +2 -2
- package/agent-assets/skills/external-services/references/calendar-outlook.md +1 -1
- package/agent-assets/skills/external-services/references/exec-errors.md +32 -0
- package/agent-assets/skills/external-services/references/obsidian.md +2 -2
- package/agent-assets/skills/external-services/references/skills-crud.md +5 -5
- package/agent-assets/skills/gmail-lifestyle/SKILL.md +11 -83
- package/agent-assets/skills/gmail-lifestyle/references/receipts-api.md +4 -0
- package/agent-assets/skills/gmail-lifestyle/references/travel-bookings-api.md +9 -0
- package/agent-assets/skills/mail/SKILL.delegated.claude.md +15 -18
- package/agent-assets/skills/mail/SKILL.delegated.codex.md +11 -6
- package/agent-assets/skills/mail/SKILL.delegated.gemini.md +11 -6
- package/agent-assets/skills/mail/SKILL.md +10 -18
- package/agent-assets/skills/mail/SKILL.native.claude.md +8 -7
- package/agent-assets/skills/mail/SKILL.native.codex.md +1 -1
- package/agent-assets/skills/mail/SKILL.native.gemini.md +1 -1
- package/agent-assets/skills/mail/references/api.md +10 -3
- package/agent-assets/skills/mail/references/examples.md +2 -1
- package/agent-assets/skills/mail/references/providers.md +1 -1
- package/agent-assets/skills/managed-tasks/SKILL.md +48 -81
- package/agent-assets/skills/managed-tasks/references/errors.md +33 -19
- package/agent-assets/skills/managed-tasks/references/output-path.md +33 -17
- package/agent-assets/skills/managed-tasks/references/recurrence-rule.md +7 -5
- package/agent-assets/skills/management-policy/SKILL.md +42 -42
- package/agent-assets/skills/management-policy/curation.json +1 -1
- package/agent-assets/skills/management-policy/references/policy-workflow.md +11 -12
- package/agent-assets/skills/management-policy/seeds/policy-file-shape.seed.json +1 -1
- package/agent-assets/skills/notify/SKILL.md +14 -16
- package/agent-assets/skills/notify/references/priority.md +28 -20
- package/agent-assets/skills/notion/SKILL.delegated.claude.md +2 -2
- package/agent-assets/skills/notion/SKILL.delegated.codex.md +2 -2
- package/agent-assets/skills/notion/SKILL.delegated.gemini.md +2 -2
- package/agent-assets/skills/notion/SKILL.md +17 -17
- package/agent-assets/skills/notion/SKILL.native.claude.md +11 -7
- package/agent-assets/skills/notion/SKILL.native.codex.md +10 -5
- package/agent-assets/skills/notion/SKILL.native.gemini.md +10 -5
- package/agent-assets/skills/observations/SKILL.md +29 -28
- package/agent-assets/skills/observations/references/fetch-fallback.md +22 -0
- package/agent-assets/skills/project-doc/SKILL.md +10 -7
- package/agent-assets/skills/project-doc/curation.json +3 -3
- package/agent-assets/skills/project-doc/seeds/project-shape.seed.json +8 -5
- package/agent-assets/skills/project-doc/seeds/slug-grammar.seed.json +5 -5
- package/agent-assets/skills/reading/SKILL.md +18 -42
- package/agent-assets/skills/reading/references/reading-taste.md +7 -7
- package/agent-assets/skills/roadmap/SKILL.md +8 -24
- package/agent-assets/skills/roadmap/curation.json +1 -1
- package/agent-assets/skills/roadmap/references/api.md +21 -11
- package/agent-assets/skills/roadmap/references/cross-check.md +15 -8
- package/agent-assets/skills/roadmap/references/horizon-tags.md +11 -0
- package/agent-assets/skills/roadmap/references/migration.md +12 -10
- package/agent-assets/skills/roadmap/references/retention.md +18 -0
- package/agent-assets/skills/roadmap/seeds/entry-types.seed.json +1 -1
- package/agent-assets/skills/schedule/SKILL.md +41 -50
- package/agent-assets/skills/schedule/references/batch.md +2 -2
- package/agent-assets/skills/schedule/references/errors.md +7 -4
- package/agent-assets/skills/schedule/references/importance.md +23 -0
- package/agent-assets/skills/schedule/references/model-selection.md +3 -3
- package/agent-assets/skills/schedule/references/recurrence-rule.md +7 -5
- package/agent-assets/skills/scheduled-managed-task/SKILL.md +77 -70
- package/agent-assets/skills/today/SKILL.md +24 -83
- package/agent-assets/skills/today/curation.json +3 -3
- package/agent-assets/skills/today/references/agent-plan-lifecycle.md +6 -5
- package/agent-assets/skills/today/references/today-skeleton.md +66 -0
- package/agent-assets/skills/today/seeds/agent-notes-flavors.seed.json +1 -1
- package/agent-assets/skills/today/seeds/section-shape.seed.json +7 -7
- package/agent-assets/skills/user-interview/SKILL.md +21 -93
- package/agent-assets/skills/user-interview/references/op-briefing.md +3 -3
- package/agent-assets/skills/user-interview/references/op-dm-handler.md +88 -0
- package/agent-assets/skills/user-interview/references/op-morning.md +1 -1
- package/agent-assets/skills/user-interview/references/sweep-and-fallback.md +9 -1
- package/agent-assets/skills/user-profile/SKILL.md +29 -39
- package/agent-assets/skills/user-profile/curation.json +4 -4
- package/agent-assets/skills/user-profile/references/character-preferences.md +3 -3
- package/agent-assets/skills/user-profile/seeds/routing-table.seed.json +8 -8
- package/agent-assets/skills/user-profile/seeds/topic-files.seed.json +6 -6
- package/agent-assets/skills/wiki/wiki-ask/SKILL.md +1 -1
- package/agent-assets/skills/wiki/wiki-compile/SKILL.md +9 -8
- package/agent-assets/skills/wiki/wiki-connect/SKILL.md +32 -5
- package/agent-assets/skills/wiki/wiki-ingest/SKILL.md +6 -50
- package/agent-assets/skills/wiki/wiki-ingest/references/curl-errors.md +58 -0
- package/agent-assets/skills/wiki/wiki-lint/SKILL.md +20 -14
- package/agent-assets/skills/wiki/wiki-trace/SKILL.md +10 -5
- package/agent-assets/skills/wiki/wiki-vault-rules/SKILL.md +2 -0
- package/agent-assets/system-prompts/routine-fetch-window.md +22 -12
- package/agent-assets/task-flows/_partials/calendar-acquire.google_calendar.md +4 -2
- package/agent-assets/task-flows/_partials/calendar-acquire.outlook_calendar.md +4 -2
- package/agent-assets/task-flows/_partials/capture-user-info.md +2 -2
- package/agent-assets/task-flows/_partials/dm-intent.long-horizon.md +1 -1
- package/agent-assets/task-flows/_partials/dm-intent.project.md +9 -9
- package/agent-assets/task-flows/_partials/feedback-capture.md +30 -0
- package/agent-assets/task-flows/_partials/mail-acquire.outlook_mail.md +3 -2
- package/agent-assets/task-flows/_partials/notion-acquire.notion.md +10 -5
- package/agent-assets/task-flows/browser_task.md +84 -0
- package/agent-assets/task-flows/github.assigned.md +1 -1
- package/agent-assets/task-flows/github.pull_request.review_requested.md +2 -2
- package/agent-assets/task-flows/github.workflow_run.failed.md +2 -2
- package/agent-assets/task-flows/knowledge.import.md +14 -14
- package/agent-assets/task-flows/message.received.dm.md +13 -4
- package/agent-assets/task-flows/message.received.dm_first.md +7 -3
- package/agent-assets/task-flows/routine.custom.md +3 -3
- package/agent-assets/task-flows/routine.evening_review.md +88 -8
- package/agent-assets/task-flows/routine.fetch_window.md +2 -2
- package/agent-assets/task-flows/routine.hourly_check.md +16 -12
- package/agent-assets/task-flows/routine.monthly_review.md +93 -21
- package/agent-assets/task-flows/routine.morning_routine_journal.md +119 -97
- package/agent-assets/task-flows/routine.morning_routine_today.md +43 -43
- package/agent-assets/task-flows/routine.research_cluster_update.md +35 -0
- package/agent-assets/task-flows/routine.research_dispatch.md +38 -0
- package/agent-assets/task-flows/routine.research_offer_dm.md +125 -0
- package/agent-assets/task-flows/routine.research_wiki_summary.md +53 -0
- package/agent-assets/task-flows/routine.roadmap_refresh.md +10 -10
- package/agent-assets/task-flows/routine.today_refresh.md +4 -4
- package/agent-assets/task-flows/routine.user_profile_sweep.md +10 -10
- package/agent-assets/task-flows/routine.weekly_review.md +114 -24
- package/agent-assets/task-flows/schedule.approaching.md +0 -1
- package/agent-assets/task-flows/scheduled.dm.md +5 -5
- package/agent-assets/task-flows/scheduled.task.md +4 -4
- package/agent-assets/task-flows/setup.initial.md +21 -21
- package/agent-assets/task-flows/setup.update.md +2 -2
- package/agent-assets/task-flows/wiki.trace.md +1 -1
- package/agent-assets/templates/README.md +27 -20
- package/agent-assets/templates/_index.md +42 -26
- package/agent-assets/templates/_manifest.json +34 -99
- package/agent-assets/templates/{user → identity}/_index.md +1 -1
- package/agent-assets/templates/{user → identity}/profile.md +2 -2
- package/agent-assets/templates/{dossiers → knowledge/dossiers}/_index.md +1 -1
- package/agent-assets/templates/{projects → plans/projects}/_active.base +1 -1
- package/agent-assets/templates/policies/_index.md +21 -0
- package/agent-assets/templates/{rules → policies}/journal-export.md +1 -1
- package/agent-assets/templates/{rules → policies}/journal-format.md +5 -5
- package/agent-assets/templates/{rules/policies → policies/management-captures}/_index.md +2 -2
- package/agent-assets/templates/{rules → policies}/management.md +3 -3
- package/agent-assets/templates/{rules → policies}/mcp.md +1 -1
- package/agent-assets/templates/{rules → policies}/redaction.md +1 -1
- package/agent-assets/templates/{routines → policies/routines}/_index.md +1 -1
- package/agent-assets/templates/{routines → policies/routines}/evening.md +2 -2
- package/agent-assets/templates/{routines → policies/routines}/hourly.md +1 -1
- package/agent-assets/templates/{routines → policies/routines}/monthly.md +2 -2
- package/bin/aitne.mjs +58 -15
- package/package.json +5 -4
- package/scripts/commands/doctor.mjs +25 -10
- package/scripts/commands/run-now.mjs +6 -21
- package/scripts/lib/ports.d.mts +27 -0
- package/scripts/lib/ports.mjs +36 -0
- package/scripts/lib/process-identity.d.mts +46 -0
- package/scripts/lib/process-identity.mjs +193 -0
- package/scripts/lib/read-api-token.mjs +176 -0
- package/scripts/start.mjs +16 -5
- package/agent-assets/docs/features/lifestyle/travel-time.md +0 -58
- package/agent-assets/skills/gmail-lifestyle/references/travel-time-api.md +0 -59
- package/agent-assets/skills/schedule/references/recurring.md +0 -185
- package/agent-assets/templates/context-index.md +0 -42
- package/agent-assets/templates/rules/_index.md +0 -19
- /package/agent-assets/templates/{user → identity}/expertise.md +0 -0
- /package/agent-assets/templates/{user → identity}/goals.md +0 -0
- /package/agent-assets/templates/{user → identity}/people.md +0 -0
- /package/agent-assets/templates/{user → identity}/personal.md +0 -0
- /package/agent-assets/templates/{user → identity}/work.md +0 -0
- /package/agent-assets/templates/{agent/journal.md → journal/agent.md} +0 -0
- /package/agent-assets/templates/{dossiers → knowledge/dossiers}/evening.md +0 -0
- /package/agent-assets/templates/{dossiers → knowledge/dossiers}/hourly.md +0 -0
- /package/agent-assets/templates/{dossiers → knowledge/dossiers}/monthly.md +0 -0
- /package/agent-assets/templates/{dossiers → knowledge/dossiers}/morning.md +0 -0
- /package/agent-assets/templates/{dossiers → knowledge/dossiers}/roadmap.md +0 -0
- /package/agent-assets/templates/{dossiers → knowledge/dossiers}/weekly.md +0 -0
- /package/agent-assets/templates/{projects → plans/projects}/_index.md +0 -0
- /package/agent-assets/templates/{roadmap.md → plans/roadmap.md} +0 -0
- /package/agent-assets/templates/{routines → policies/routines}/morning.md +0 -0
- /package/agent-assets/templates/{routines → policies/routines}/weekly.md +0 -0
- /package/agent-assets/templates/{agent → state}/profile-questions.md +0 -0
- /package/agent-assets/templates/{today.md → state/today.md} +0 -0
|
@@ -0,0 +1,193 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Process start-identity helpers for the Aitne launcher (`bin/aitne.mjs`).
|
|
3
|
+
*
|
|
4
|
+
* WHY THIS EXISTS — process-lifecycle-2 (CROSS_PLATFORM_REAUDIT_2026-06.md).
|
|
5
|
+
* The launcher trusts a bare PID: `process.kill(pid, 0)` is true for *any* live
|
|
6
|
+
* process owning that PID. After an unclean shutdown the stale `daemon.pid` /
|
|
7
|
+
* `dashboard.pid` can point at a PID the OS has recycled to an unrelated
|
|
8
|
+
* process, so `aitne start` false-positives "Already running" and `aitne stop`
|
|
9
|
+
* can `taskkill /T /F` (Windows) or group-kill (POSIX) the wrong process tree.
|
|
10
|
+
*
|
|
11
|
+
* FIX — pair the PID with the OS-reported process **start time**, captured at
|
|
12
|
+
* write time and re-read at check time. A recycled PID has a different start
|
|
13
|
+
* time, so an exact-string mismatch flags it stale. We compare the *same* OS
|
|
14
|
+
* field to *itself* on the *same* machine, so no absolute-time conversion,
|
|
15
|
+
* clock-skew tolerance, or locale handling is needed.
|
|
16
|
+
*
|
|
17
|
+
* WHERE THIS LIVES — `scripts/lib/` (plain ESM), because `bin/aitne.mjs` runs
|
|
18
|
+
* *before* the TypeScript build that produces `@aitne/shared` (running `aitne
|
|
19
|
+
* start` is what triggers that build) and the published package ships only
|
|
20
|
+
* `bin` + `scripts` + `agent-assets`. The pure functions here are pinned by a
|
|
21
|
+
* peer `.test.ts` under the daemon/shared `src` tree (the `ports.mjs`
|
|
22
|
+
* precedent), so they run under the standard vitest suite. The win32 read branch
|
|
23
|
+
* carries the usual "no Windows runtime validation" caveat; by construction it
|
|
24
|
+
* can only *degrade* to the legacy bare-PID behavior, never make things worse.
|
|
25
|
+
*/
|
|
26
|
+
import { execFileSync } from "node:child_process";
|
|
27
|
+
import { accessSync, constants, readFileSync } from "node:fs";
|
|
28
|
+
import { delimiter, join } from "node:path";
|
|
29
|
+
import process from "node:process";
|
|
30
|
+
|
|
31
|
+
/**
|
|
32
|
+
* Serialize PID metadata to the pidfile.
|
|
33
|
+
*
|
|
34
|
+
* Line 1 is the bare PID so an *older* aitne (which parses only line 1) still
|
|
35
|
+
* reads files written by this version — downgrade-safe. The identity token is
|
|
36
|
+
* written as a trailing `key=value` line (not positional) so a future field can
|
|
37
|
+
* be added without breaking parse order; {@link parsePidMeta} already ignores
|
|
38
|
+
* unknown keys. An absent token is omitted.
|
|
39
|
+
*/
|
|
40
|
+
export function serializePidMeta({ pid, startToken = null }) {
|
|
41
|
+
let out = `${pid}\n`;
|
|
42
|
+
if (startToken != null) {
|
|
43
|
+
const token = String(startToken).replace(/[\r\n]+/g, " ").trim();
|
|
44
|
+
if (token.length > 0) out += `start=${token}\n`;
|
|
45
|
+
}
|
|
46
|
+
return out;
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
/**
|
|
50
|
+
* Parse a pidfile written by {@link serializePidMeta} OR a legacy single-line
|
|
51
|
+
* (`<pid>\n`) file. Returns `null` if line 1 is not a finite integer. Unknown
|
|
52
|
+
* trailer keys are ignored, so the format stays forward-compatible.
|
|
53
|
+
*/
|
|
54
|
+
export function parsePidMeta(content) {
|
|
55
|
+
if (typeof content !== "string") return null;
|
|
56
|
+
const lines = content.split(/\r?\n/);
|
|
57
|
+
const pid = Number.parseInt((lines[0] ?? "").trim(), 10);
|
|
58
|
+
if (!Number.isFinite(pid)) return null;
|
|
59
|
+
let startToken = null;
|
|
60
|
+
for (const line of lines.slice(1)) {
|
|
61
|
+
const eq = line.indexOf("=");
|
|
62
|
+
if (eq < 0) continue;
|
|
63
|
+
const key = line.slice(0, eq).trim();
|
|
64
|
+
const val = line.slice(eq + 1).trim();
|
|
65
|
+
if (key === "start") startToken = val.length > 0 ? val : null;
|
|
66
|
+
}
|
|
67
|
+
return { pid, startToken };
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
/**
|
|
71
|
+
* Extract field 22 (`starttime`, clock-ticks since boot) from `/proc/<pid>/stat`.
|
|
72
|
+
*
|
|
73
|
+
* The `comm` field (field 2) is wrapped in parens and may itself contain spaces
|
|
74
|
+
* and `)`, so the only safe split point is the LAST `)` — everything after it
|
|
75
|
+
* is space-separated starting at field 3 (state). field 22 is therefore index
|
|
76
|
+
* `22 - 3 = 19` in that tail. Boot-relative, so immune to wall-clock changes;
|
|
77
|
+
* across a reboot the recorded value's epoch is gone, which correctly reads as
|
|
78
|
+
* a mismatch (stale).
|
|
79
|
+
*/
|
|
80
|
+
export function parseLinuxStat(statContent) {
|
|
81
|
+
if (typeof statContent !== "string") return null;
|
|
82
|
+
const close = statContent.lastIndexOf(")");
|
|
83
|
+
if (close < 0) return null;
|
|
84
|
+
const fields = statContent.slice(close + 1).trim().split(/\s+/);
|
|
85
|
+
const starttime = fields[19];
|
|
86
|
+
return starttime && /^\d+$/.test(starttime) ? starttime : null;
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
/**
|
|
90
|
+
* Resolve the PowerShell executable on Windows. Prefer Windows PowerShell 5.1
|
|
91
|
+
* (`powershell.exe`), fall back to PowerShell 7+ (`pwsh.exe`) for minimal /
|
|
92
|
+
* Server-Core / pwsh-7-only hosts, else keep the default so a missing host
|
|
93
|
+
* surfaces a clear ENOENT. Mirrors `browser-history/lifecycle/platform.ts`.
|
|
94
|
+
*/
|
|
95
|
+
/* c8 ignore start -- win32-only path resolution; the POSIX test runner never enters this */
|
|
96
|
+
function resolveWindowsPowerShell() {
|
|
97
|
+
const pathValue = process.env.PATH ?? "";
|
|
98
|
+
const exts = process.env.PATHEXT?.split(";").filter(Boolean) ?? [".EXE"];
|
|
99
|
+
const probe = (name) => {
|
|
100
|
+
const hasExt = /\.[A-Za-z0-9]+$/.test(name);
|
|
101
|
+
for (const dir of pathValue.split(delimiter)) {
|
|
102
|
+
if (!dir) continue;
|
|
103
|
+
const candidates = hasExt ? [name] : exts.map((e) => `${name}${e}`);
|
|
104
|
+
for (const c of candidates) {
|
|
105
|
+
try {
|
|
106
|
+
accessSync(join(dir, c), constants.X_OK);
|
|
107
|
+
return true;
|
|
108
|
+
} catch {
|
|
109
|
+
// keep scanning
|
|
110
|
+
}
|
|
111
|
+
}
|
|
112
|
+
}
|
|
113
|
+
return false;
|
|
114
|
+
};
|
|
115
|
+
return probe("powershell.exe") ? "powershell.exe" : probe("pwsh.exe") ? "pwsh.exe" : "powershell.exe";
|
|
116
|
+
}
|
|
117
|
+
/* c8 ignore stop */
|
|
118
|
+
|
|
119
|
+
/**
|
|
120
|
+
* Read an opaque, OS-native start-time token for `pid`, or `null` if the PID is
|
|
121
|
+
* gone / the read fails. The token is compared *only* for exact-string equality
|
|
122
|
+
* against a token captured earlier for the same PID on the same machine, so its
|
|
123
|
+
* format is irrelevant as long as it is stable for a given process incarnation.
|
|
124
|
+
*
|
|
125
|
+
* `deps` is for testing — inject `platform` / `execFileSync` / `readFileSync`
|
|
126
|
+
* to exercise a branch off its native OS without touching the real system.
|
|
127
|
+
* Reads use `execFileSync` (never a shell) with a timeout; the PID is numeric,
|
|
128
|
+
* so there is no injection surface, but args stay arrayed on principle.
|
|
129
|
+
*/
|
|
130
|
+
export function readProcessStartToken(pid, deps = {}) {
|
|
131
|
+
const platform = deps.platform ?? process.platform;
|
|
132
|
+
const exec = deps.execFileSync ?? execFileSync;
|
|
133
|
+
const readFile = deps.readFileSync ?? readFileSync;
|
|
134
|
+
if (pid == null || !Number.isFinite(Number(pid))) return null;
|
|
135
|
+
try {
|
|
136
|
+
if (platform === "linux") {
|
|
137
|
+
return parseLinuxStat(readFile(`/proc/${pid}/stat`, "utf8"));
|
|
138
|
+
}
|
|
139
|
+
if (platform === "darwin") {
|
|
140
|
+
const out = exec("ps", ["-o", "lstart=", "-p", String(pid)], {
|
|
141
|
+
stdio: ["ignore", "pipe", "ignore"],
|
|
142
|
+
timeout: 5_000,
|
|
143
|
+
windowsHide: true,
|
|
144
|
+
});
|
|
145
|
+
const token = String(out).replace(/[\r\n]+/g, " ").trim();
|
|
146
|
+
return token.length > 0 ? token : null;
|
|
147
|
+
}
|
|
148
|
+
/* c8 ignore start -- win32-only; not reachable from the POSIX test runner */
|
|
149
|
+
if (platform === "win32") {
|
|
150
|
+
const ps = resolveWindowsPowerShell();
|
|
151
|
+
const out = exec(
|
|
152
|
+
ps,
|
|
153
|
+
[
|
|
154
|
+
"-NoProfile",
|
|
155
|
+
"-NonInteractive",
|
|
156
|
+
"-Command",
|
|
157
|
+
`(Get-Process -Id ${Number(pid)} -ErrorAction Stop).StartTime.ToString('o')`,
|
|
158
|
+
],
|
|
159
|
+
{ stdio: ["ignore", "pipe", "ignore"], timeout: 5_000, windowsHide: true },
|
|
160
|
+
);
|
|
161
|
+
const token = String(out).replace(/[\r\n]+/g, " ").trim();
|
|
162
|
+
return token.length > 0 ? token : null;
|
|
163
|
+
}
|
|
164
|
+
/* c8 ignore stop */
|
|
165
|
+
return null;
|
|
166
|
+
} catch {
|
|
167
|
+
return null;
|
|
168
|
+
}
|
|
169
|
+
}
|
|
170
|
+
|
|
171
|
+
/**
|
|
172
|
+
* Classify a pidfile's recorded process against the live system.
|
|
173
|
+
*
|
|
174
|
+
* - `stale` — dead, or alive but the start-time token *differs*
|
|
175
|
+
* (PID recycled). Caller should remove the pidfile.
|
|
176
|
+
* - `running-ours` — alive and the start-time token matches.
|
|
177
|
+
* - `running-unverified` — alive but identity can't be confirmed: a legacy file
|
|
178
|
+
* with no token, or the OS start-time read failed.
|
|
179
|
+
* Caller treats this as running (the pre-fix bare-PID
|
|
180
|
+
* behavior), so the change can never regress; a legacy
|
|
181
|
+
* file self-heals on the next `writePid`.
|
|
182
|
+
*
|
|
183
|
+
* `isAlive` / `readToken` are injected so the decision is pure and fully
|
|
184
|
+
* unit-testable without touching real processes.
|
|
185
|
+
*/
|
|
186
|
+
export function classifyPid(meta, { readToken, isAlive }) {
|
|
187
|
+
if (meta == null || meta.pid == null) return "stale";
|
|
188
|
+
if (!isAlive(meta.pid)) return "stale";
|
|
189
|
+
if (meta.startToken == null) return "running-unverified";
|
|
190
|
+
const live = readToken(meta.pid);
|
|
191
|
+
if (live == null) return "running-unverified";
|
|
192
|
+
return live === meta.startToken ? "running-ours" : "stale";
|
|
193
|
+
}
|
|
@@ -0,0 +1,176 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Cross-platform reader for the daemon's `apiToken` secret.
|
|
3
|
+
*
|
|
4
|
+
* CLI scripts (`run-now`, `remint-roadmap-ids`) need the daemon's apiToken to
|
|
5
|
+
* call Approve-tier `/api/...` routes, but they intentionally must run WITHOUT
|
|
6
|
+
* a successful `@aitne/shared` build (which only ships `./dist/*.js`). So this
|
|
7
|
+
* helper re-implements the per-OS read inline, mirroring the secret clients in
|
|
8
|
+
* `packages/shared/src/`:
|
|
9
|
+
*
|
|
10
|
+
* - darwin → macOS Keychain via `security` (service com.personal-agent.secret.apiToken)
|
|
11
|
+
* - win32 → DPAPI: decrypt ~/.personal-agent/secrets/apiToken.dpapi via PowerShell
|
|
12
|
+
* - linux → libsecret (`secret-tool lookup`), else the AES-256-GCM file store
|
|
13
|
+
* - WSL / other → the AES-256-GCM file store
|
|
14
|
+
*
|
|
15
|
+
* The file-store format/params and the DPAPI script are copied verbatim from
|
|
16
|
+
* `secret-client-file.ts` / `secret-client-windows.ts` — keep them in sync if
|
|
17
|
+
* those change. The secret clients hardcode `~/.personal-agent/secrets` (they
|
|
18
|
+
* do NOT honor PA_DATA_DIR), so this helper uses the same homedir-relative
|
|
19
|
+
* location to find what the daemon actually wrote.
|
|
20
|
+
*
|
|
21
|
+
* Returns the token string, or null when it cannot be read; the caller decides
|
|
22
|
+
* how to message the failure. Best-effort: never throws (so a CLI never dies
|
|
23
|
+
* with a raw stack trace on a misconfigured secret store).
|
|
24
|
+
*/
|
|
25
|
+
import { execFileSync } from "node:child_process";
|
|
26
|
+
import { existsSync, readFileSync, statSync } from "node:fs";
|
|
27
|
+
import { homedir } from "node:os";
|
|
28
|
+
import { join } from "node:path";
|
|
29
|
+
import { createDecipheriv, scryptSync } from "node:crypto";
|
|
30
|
+
|
|
31
|
+
const SECRET_NAME = "apiToken";
|
|
32
|
+
const KEYCHAIN_SERVICE = "com.personal-agent.secret.apiToken";
|
|
33
|
+
|
|
34
|
+
function secretsDir() {
|
|
35
|
+
return join(homedir(), ".personal-agent", "secrets");
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
/** @returns {string | null} the daemon apiToken, or null if unreadable. */
|
|
39
|
+
export function readApiToken() {
|
|
40
|
+
const platform = process.platform;
|
|
41
|
+
if (platform === "darwin") return readDarwin();
|
|
42
|
+
if (platform === "win32") return readWindows();
|
|
43
|
+
if (platform === "linux") {
|
|
44
|
+
if (!isWsl() && whichSync("secret-tool")) {
|
|
45
|
+
const fromKeyring = readSecretTool();
|
|
46
|
+
if (fromKeyring) return fromKeyring;
|
|
47
|
+
}
|
|
48
|
+
return readFileStore();
|
|
49
|
+
}
|
|
50
|
+
return readFileStore();
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
function readDarwin() {
|
|
54
|
+
// Byte-identical to the historical `security` read so macOS cannot regress.
|
|
55
|
+
try {
|
|
56
|
+
return execFileSync(
|
|
57
|
+
"security",
|
|
58
|
+
["find-generic-password", "-s", KEYCHAIN_SERVICE, "-w"],
|
|
59
|
+
{ encoding: "utf-8", timeout: 5_000 },
|
|
60
|
+
).trim();
|
|
61
|
+
} catch {
|
|
62
|
+
return null;
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
/** Cross-platform `which`, returning the resolved path or null. */
|
|
67
|
+
function whichSync(cmd) {
|
|
68
|
+
const tool = process.platform === "win32" ? "where" : "which";
|
|
69
|
+
try {
|
|
70
|
+
const out = execFileSync(tool, [cmd], {
|
|
71
|
+
stdio: ["ignore", "pipe", "ignore"],
|
|
72
|
+
timeout: 2_000,
|
|
73
|
+
});
|
|
74
|
+
return out.toString().split(/\r?\n/)[0]?.trim() || null;
|
|
75
|
+
} catch {
|
|
76
|
+
return null;
|
|
77
|
+
}
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
function readWindows() {
|
|
81
|
+
const path = join(secretsDir(), `${SECRET_NAME}.dpapi`);
|
|
82
|
+
if (!existsSync(path)) return null;
|
|
83
|
+
const encrypted = readFileSync(path, "utf-8").trim();
|
|
84
|
+
// Prefer in-box Windows PowerShell 5.1, fall back to PowerShell 7+ (pwsh),
|
|
85
|
+
// matching the daemon's secret-client-factory resolution order.
|
|
86
|
+
const psBinary = whichSync("powershell.exe")
|
|
87
|
+
? "powershell.exe"
|
|
88
|
+
: whichSync("pwsh.exe")
|
|
89
|
+
? "pwsh.exe"
|
|
90
|
+
: "powershell.exe";
|
|
91
|
+
// Mirrors WindowsDpapiSecretClient.get(): DPAPI-decrypt via
|
|
92
|
+
// ConvertTo-SecureString and marshal back to plaintext. The ciphertext is
|
|
93
|
+
// passed via stdin, never interpolated into the script, to avoid injection.
|
|
94
|
+
const script = [
|
|
95
|
+
"$enc = [System.Console]::In.ReadToEnd().Trim()",
|
|
96
|
+
"$ss = ConvertTo-SecureString $enc",
|
|
97
|
+
"$bstr = [System.Runtime.InteropServices.Marshal]::SecureStringToBSTR($ss)",
|
|
98
|
+
"try { [System.Runtime.InteropServices.Marshal]::PtrToStringAuto($bstr) }",
|
|
99
|
+
"finally { [System.Runtime.InteropServices.Marshal]::ZeroFreeBSTR($bstr) }",
|
|
100
|
+
].join("; ");
|
|
101
|
+
try {
|
|
102
|
+
const out = execFileSync(
|
|
103
|
+
psBinary,
|
|
104
|
+
["-NoProfile", "-NonInteractive", "-Command", script],
|
|
105
|
+
{ input: encrypted, encoding: "utf-8", timeout: 10_000 },
|
|
106
|
+
);
|
|
107
|
+
return out.trimEnd() || null;
|
|
108
|
+
} catch {
|
|
109
|
+
return null;
|
|
110
|
+
}
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
function readSecretTool() {
|
|
114
|
+
try {
|
|
115
|
+
const out = execFileSync(
|
|
116
|
+
"secret-tool",
|
|
117
|
+
["lookup", "service", "personal-agent", "key", SECRET_NAME],
|
|
118
|
+
{ encoding: "utf-8", timeout: 5_000 },
|
|
119
|
+
);
|
|
120
|
+
return out.replace(/\n$/, "") || null;
|
|
121
|
+
} catch {
|
|
122
|
+
return null;
|
|
123
|
+
}
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
/**
|
|
127
|
+
* Detect WSL. WSL reports platform "linux" but cannot use `secret-tool`
|
|
128
|
+
* (D-Bus / GNOME Keyring typically unavailable), so it uses the file store.
|
|
129
|
+
*/
|
|
130
|
+
function isWsl() {
|
|
131
|
+
try {
|
|
132
|
+
return /microsoft|wsl/i.test(readFileSync("/proc/version", "utf-8"));
|
|
133
|
+
} catch {
|
|
134
|
+
return false;
|
|
135
|
+
}
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
// ── AES-256-GCM file store (mirrors secret-client-file.ts) ────────────────
|
|
139
|
+
const FILE_ALGORITHM = "aes-256-gcm";
|
|
140
|
+
const FILE_KEY_LENGTH = 32; // 256 bits
|
|
141
|
+
const FILE_SCRYPT = { N: 16384, r: 8, p: 1 };
|
|
142
|
+
|
|
143
|
+
/** Resolve the file-store master password (env, then key file); null if none. */
|
|
144
|
+
function resolveMasterPassword() {
|
|
145
|
+
if (process.env.PA_MASTER_PASSWORD) return process.env.PA_MASTER_PASSWORD;
|
|
146
|
+
const keyFilePath = join(secretsDir(), ".master-key");
|
|
147
|
+
if (!existsSync(keyFilePath)) return null;
|
|
148
|
+
// Refuse to read a key file with insecure permissions (mirrors the daemon's
|
|
149
|
+
// 0600/0400 gate); degrade to null rather than risk exposing it.
|
|
150
|
+
const mode = statSync(keyFilePath).mode & 0o777;
|
|
151
|
+
if (mode !== 0o600 && mode !== 0o400) return null;
|
|
152
|
+
return readFileSync(keyFilePath, "utf-8").trim();
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
function readFileStore() {
|
|
156
|
+
const path = join(secretsDir(), `${SECRET_NAME}.enc`);
|
|
157
|
+
if (!existsSync(path)) return null;
|
|
158
|
+
try {
|
|
159
|
+
const password = resolveMasterPassword();
|
|
160
|
+
if (!password) return null;
|
|
161
|
+
const stored = JSON.parse(readFileSync(path, "utf-8"));
|
|
162
|
+
const salt = Buffer.from(stored.salt, "hex");
|
|
163
|
+
const iv = Buffer.from(stored.iv, "hex");
|
|
164
|
+
const authTag = Buffer.from(stored.authTag, "hex");
|
|
165
|
+
const ciphertext = Buffer.from(stored.ciphertext, "hex");
|
|
166
|
+
const key = scryptSync(password, salt, FILE_KEY_LENGTH, FILE_SCRYPT);
|
|
167
|
+
const decipher = createDecipheriv(FILE_ALGORITHM, key, iv);
|
|
168
|
+
decipher.setAuthTag(authTag);
|
|
169
|
+
return Buffer.concat([
|
|
170
|
+
decipher.update(ciphertext),
|
|
171
|
+
decipher.final(),
|
|
172
|
+
]).toString("utf-8");
|
|
173
|
+
} catch {
|
|
174
|
+
return null;
|
|
175
|
+
}
|
|
176
|
+
}
|
package/scripts/start.mjs
CHANGED
|
@@ -6,6 +6,7 @@ import path from "node:path";
|
|
|
6
6
|
import process from "node:process";
|
|
7
7
|
import { ensureBuild, log } from "./run-node.mjs";
|
|
8
8
|
import { openBrowser, waitForHttpReady } from "./browser.mjs";
|
|
9
|
+
import { resolveDashboardPort } from "./lib/ports.mjs";
|
|
9
10
|
|
|
10
11
|
const IS_WINDOWS = process.platform === "win32";
|
|
11
12
|
const requireFromScript = createRequire(import.meta.url);
|
|
@@ -57,7 +58,7 @@ function nextSpawnArgs(dashboardDir, nextBin, userArgs) {
|
|
|
57
58
|
* 5. Ctrl+C stops both
|
|
58
59
|
*/
|
|
59
60
|
|
|
60
|
-
const DASHBOARD_PORT =
|
|
61
|
+
const DASHBOARD_PORT = resolveDashboardPort();
|
|
61
62
|
const noOpen = process.argv.slice(2).includes("--no-open");
|
|
62
63
|
const children = [];
|
|
63
64
|
let shuttingDown = false;
|
|
@@ -92,21 +93,31 @@ try {
|
|
|
92
93
|
}
|
|
93
94
|
const nextBin = resolveNextBin(dashboardDir);
|
|
94
95
|
const dashArgs = nextSpawnArgs(dashboardDir, nextBin, [
|
|
95
|
-
|
|
96
|
+
// Bind IPv4 loopback explicitly: with no --hostname, Next binds the
|
|
97
|
+
// unspecified IPv6 address `::`, and on Windows (IPV6_V6ONLY on) the
|
|
98
|
+
// 127.0.0.1 readiness probe in bin/aitne.mjs can't reach it. Match the
|
|
99
|
+
// probe and the daemon's 127.0.0.1-only posture. Byte-identical on
|
|
100
|
+
// macOS/Linux where the probe already reaches a `::`-bound socket.
|
|
101
|
+
"dev", "--port", String(DASHBOARD_PORT), "--hostname", "127.0.0.1",
|
|
96
102
|
]);
|
|
97
|
-
const
|
|
103
|
+
const useShimShell = IS_WINDOWS && nextBin.toLowerCase().endsWith(".cmd");
|
|
104
|
+
const dashCommand = useShimShell ? `"${nextBin}"` : nextBin;
|
|
105
|
+
const dashboard = spawn(dashCommand, dashArgs, {
|
|
98
106
|
cwd: dashboardDir,
|
|
99
107
|
env: process.env,
|
|
100
108
|
stdio: "inherit",
|
|
101
109
|
windowsHide: true,
|
|
102
|
-
shell:
|
|
110
|
+
shell: useShimShell,
|
|
103
111
|
});
|
|
104
112
|
children.push(dashboard);
|
|
105
113
|
|
|
106
114
|
// ── 4. Auto-open browser ──
|
|
107
115
|
|
|
108
116
|
if (!noOpen) {
|
|
109
|
-
|
|
117
|
+
// Match the dashboard's 127.0.0.1-only bind above. Using `localhost` here
|
|
118
|
+
// resolves to `::1` first on Windows, so both the readiness probe AND the
|
|
119
|
+
// browser open would miss the IPv4-only socket and time out.
|
|
120
|
+
const url = `http://127.0.0.1:${DASHBOARD_PORT}`;
|
|
110
121
|
waitForHttpReady(url, {
|
|
111
122
|
// `next dev` can take ~30s on a cold boot; give it headroom.
|
|
112
123
|
timeoutMs: 60_000,
|
|
@@ -1,58 +0,0 @@
|
|
|
1
|
-
---
|
|
2
|
-
schema_version: 1
|
|
3
|
-
slug: features/lifestyle/travel-time
|
|
4
|
-
title: Travel Time
|
|
5
|
-
id: travel-time
|
|
6
|
-
aliases:
|
|
7
|
-
- door to door
|
|
8
|
-
- eta
|
|
9
|
-
category: features
|
|
10
|
-
summary: |
|
|
11
|
-
A skill that estimates door-to-door travel time given an origin
|
|
12
|
-
and destination — used by schedule-approaching reminders for
|
|
13
|
-
events with location.
|
|
14
|
-
section: lifestyle
|
|
15
|
-
tags:
|
|
16
|
-
- lifestyle
|
|
17
|
-
- travel
|
|
18
|
-
- skills
|
|
19
|
-
status: stable
|
|
20
|
-
ask_examples:
|
|
21
|
-
- How long will it take me to get to the airport?
|
|
22
|
-
- Does the agent know about traffic?
|
|
23
|
-
locale: en-US
|
|
24
|
-
created: 2026-04-25
|
|
25
|
-
updated: 2026-04-25
|
|
26
|
-
keywords:
|
|
27
|
-
- travel time
|
|
28
|
-
- departure time
|
|
29
|
-
- google maps
|
|
30
|
-
- commute
|
|
31
|
-
- ETA
|
|
32
|
-
related:
|
|
33
|
-
- features/lifestyle/travel-bookings
|
|
34
|
-
- features/operations/schedule-approaching
|
|
35
|
-
---
|
|
36
|
-
|
|
37
|
-
# Travel Time
|
|
38
|
-
|
|
39
|
-
## In One Sentence
|
|
40
|
-
|
|
41
|
-
A skill the agent calls to estimate door-to-door travel time before
|
|
42
|
-
a calendar event with a location.
|
|
43
|
-
|
|
44
|
-
## What It Does
|
|
45
|
-
|
|
46
|
-
- Reads origin (current location, configured home/work) and
|
|
47
|
-
destination (event location).
|
|
48
|
-
- Returns a typical-time estimate with mode (drive, transit, walk).
|
|
49
|
-
- Used by `schedule-approaching` reminders to lead-time the alert.
|
|
50
|
-
|
|
51
|
-
## Where in the Dashboard
|
|
52
|
-
|
|
53
|
-
There is no operator surface for the travel-time data itself; the
|
|
54
|
-
estimates appear inline in event reminders and morning routines.
|
|
55
|
-
|
|
56
|
-
## Related
|
|
57
|
-
|
|
58
|
-
- [Schedule Approaching](../operations/schedule-approaching.md)
|
|
@@ -1,59 +0,0 @@
|
|
|
1
|
-
---
|
|
2
|
-
kind: reference
|
|
3
|
-
name: travel-time-api
|
|
4
|
-
description: /api/travel-time reference — Google Maps Directions wrapper. Estimate door-to-door duration and compute departure time for a calendar event with location.
|
|
5
|
-
---
|
|
6
|
-
|
|
7
|
-
# `/api/travel-time` reference
|
|
8
|
-
|
|
9
|
-
Uses the Google Maps Directions API. Prerequisite: `googleMapsApiKey`
|
|
10
|
-
configured in the daemon's secret store, with the Directions API
|
|
11
|
-
enabled.
|
|
12
|
-
|
|
13
|
-
## GET /api/travel-time
|
|
14
|
-
|
|
15
|
-
Estimate travel time between two locations.
|
|
16
|
-
|
|
17
|
-
```bash
|
|
18
|
-
# Transit (default)
|
|
19
|
-
curl -s "http://localhost:8321/api/travel-time?origin=Grand+Central&destination=Times+Square"
|
|
20
|
-
|
|
21
|
-
# Driving with arrival time
|
|
22
|
-
curl -s "http://localhost:8321/api/travel-time?origin=Brooklyn&destination=Newark&mode=driving&arrival=2026-04-12T14:00:00-04:00"
|
|
23
|
-
```
|
|
24
|
-
|
|
25
|
-
| Param | Type | Default | Description |
|
|
26
|
-
|-------|------|---------|-------------|
|
|
27
|
-
| `origin` | string | (required) | Origin address or place name |
|
|
28
|
-
| `destination` | string | (required) | Destination address or place name |
|
|
29
|
-
| `mode` | string | transit | driving, transit, walking, bicycling |
|
|
30
|
-
| `arrival` | ISO 8601 | — | Desired arrival time (computes departure time) |
|
|
31
|
-
|
|
32
|
-
Response:
|
|
33
|
-
|
|
34
|
-
```json
|
|
35
|
-
{
|
|
36
|
-
"origin": "Grand Central Terminal, NY",
|
|
37
|
-
"destination": "Times Square, NY",
|
|
38
|
-
"mode": "transit",
|
|
39
|
-
"durationSeconds": 1380,
|
|
40
|
-
"durationText": "23 mins",
|
|
41
|
-
"distanceMeters": 8500,
|
|
42
|
-
"distanceText": "8.5 km",
|
|
43
|
-
"departBy": "2026-04-12T13:34:00.000Z"
|
|
44
|
-
}
|
|
45
|
-
```
|
|
46
|
-
|
|
47
|
-
## GET /api/travel-time/for-event/:eventId
|
|
48
|
-
|
|
49
|
-
```bash
|
|
50
|
-
curl -s "http://localhost:8321/api/travel-time/for-event/abc123?origin=Home&mode=transit"
|
|
51
|
-
```
|
|
52
|
-
|
|
53
|
-
| Param | Type | Default | Description |
|
|
54
|
-
|-------|------|---------|-------------|
|
|
55
|
-
| `origin` | string | (required) | Your starting location |
|
|
56
|
-
| `mode` | string | transit | Travel mode |
|
|
57
|
-
|
|
58
|
-
Response includes both `event` and `travelTime` blocks; see the route
|
|
59
|
-
implementation for full shape.
|