@todoist/outline-cli 0.0.0-stage → 3.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +359 -0
- package/README.md +160 -2
- package/dist/commands/account.d.ts +2 -0
- package/dist/commands/account.js +107 -0
- package/dist/commands/account.js.map +1 -0
- package/dist/commands/auth.d.ts +2 -0
- package/dist/commands/auth.js +169 -0
- package/dist/commands/auth.js.map +1 -0
- package/dist/commands/changelog.d.ts +2 -0
- package/dist/commands/changelog.js +15 -0
- package/dist/commands/changelog.js.map +1 -0
- package/dist/commands/collection.d.ts +2 -0
- package/dist/commands/collection.js +106 -0
- package/dist/commands/collection.js.map +1 -0
- package/dist/commands/document.d.ts +2 -0
- package/dist/commands/document.js +291 -0
- package/dist/commands/document.js.map +1 -0
- package/dist/commands/search.d.ts +2 -0
- package/dist/commands/search.js +49 -0
- package/dist/commands/search.js.map +1 -0
- package/dist/commands/skill.d.ts +2 -0
- package/dist/commands/skill.js +80 -0
- package/dist/commands/skill.js.map +1 -0
- package/dist/commands/update/index.d.ts +2 -0
- package/dist/commands/update/index.js +14 -0
- package/dist/commands/update/index.js.map +1 -0
- package/dist/index.d.ts +2 -0
- package/dist/index.js +56 -0
- package/dist/index.js.map +1 -0
- package/dist/lib/api.d.ts +18 -0
- package/dist/lib/api.js +107 -0
- package/dist/lib/api.js.map +1 -0
- package/dist/lib/auth-constants.d.ts +18 -0
- package/dist/lib/auth-constants.js +25 -0
- package/dist/lib/auth-constants.js.map +1 -0
- package/dist/lib/auth-output.d.ts +21 -0
- package/dist/lib/auth-output.js +38 -0
- package/dist/lib/auth-output.js.map +1 -0
- package/dist/lib/auth-pages.d.ts +2 -0
- package/dist/lib/auth-pages.js +101 -0
- package/dist/lib/auth-pages.js.map +1 -0
- package/dist/lib/auth-provider.d.ts +85 -0
- package/dist/lib/auth-provider.js +363 -0
- package/dist/lib/auth-provider.js.map +1 -0
- package/dist/lib/auth.d.ts +73 -0
- package/dist/lib/auth.js +188 -0
- package/dist/lib/auth.js.map +1 -0
- package/dist/lib/config.d.ts +45 -0
- package/dist/lib/config.js +20 -0
- package/dist/lib/config.js.map +1 -0
- package/dist/lib/errors.d.ts +8 -0
- package/dist/lib/errors.js +9 -0
- package/dist/lib/errors.js.map +1 -0
- package/dist/lib/global-args.d.ts +30 -0
- package/dist/lib/global-args.js +66 -0
- package/dist/lib/global-args.js.map +1 -0
- package/dist/lib/markdown.d.ts +2 -0
- package/dist/lib/markdown.js +12 -0
- package/dist/lib/markdown.js.map +1 -0
- package/dist/lib/migrate-auth.d.ts +11 -0
- package/dist/lib/migrate-auth.js +92 -0
- package/dist/lib/migrate-auth.js.map +1 -0
- package/dist/lib/outline-account.d.ts +32 -0
- package/dist/lib/outline-account.js +22 -0
- package/dist/lib/outline-account.js.map +1 -0
- package/dist/lib/output.d.ts +15 -0
- package/dist/lib/output.js +85 -0
- package/dist/lib/output.js.map +1 -0
- package/dist/lib/refs.d.ts +33 -0
- package/dist/lib/refs.js +182 -0
- package/dist/lib/refs.js.map +1 -0
- package/dist/lib/skills/content.d.ts +3 -0
- package/dist/lib/skills/content.js +160 -0
- package/dist/lib/skills/content.js.map +1 -0
- package/dist/lib/skills/create-installer.d.ts +9 -0
- package/dist/lib/skills/create-installer.js +77 -0
- package/dist/lib/skills/create-installer.js.map +1 -0
- package/dist/lib/skills/index.d.ts +5 -0
- package/dist/lib/skills/index.js +40 -0
- package/dist/lib/skills/index.js.map +1 -0
- package/dist/lib/skills/types.d.ts +10 -0
- package/dist/lib/skills/types.js +2 -0
- package/dist/lib/skills/types.js.map +1 -0
- package/dist/lib/skills/update-installed.d.ts +6 -0
- package/dist/lib/skills/update-installed.js +23 -0
- package/dist/lib/skills/update-installed.js.map +1 -0
- package/dist/lib/spinner.d.ts +2 -0
- package/dist/lib/spinner.js +5 -0
- package/dist/lib/spinner.js.map +1 -0
- package/dist/lib/user-records.d.ts +21 -0
- package/dist/lib/user-records.js +126 -0
- package/dist/lib/user-records.js.map +1 -0
- package/dist/lib/user-ref-store.d.ts +16 -0
- package/dist/lib/user-ref-store.js +37 -0
- package/dist/lib/user-ref-store.js.map +1 -0
- package/dist/postinstall.d.ts +1 -0
- package/dist/postinstall.js +16 -0
- package/dist/postinstall.js.map +1 -0
- package/dist/transport/fetch-with-retry.d.ts +15 -0
- package/dist/transport/fetch-with-retry.js +144 -0
- package/dist/transport/fetch-with-retry.js.map +1 -0
- package/dist/transport/http-dispatcher.d.ts +37 -0
- package/dist/transport/http-dispatcher.js +95 -0
- package/dist/transport/http-dispatcher.js.map +1 -0
- package/package.json +72 -5
- package/scripts/check-skill-sync.js +40 -0
- package/scripts/postinstall.js +12 -0
- package/scripts/sync-skill.js +30 -0
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"types.js","sourceRoot":"","sources":["../../../src/lib/skills/types.ts"],"names":[],"mappings":""}
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
import { skillInstallers } from './index.js';
|
|
2
|
+
export async function updateAllInstalledSkills(local) {
|
|
3
|
+
const updated = [];
|
|
4
|
+
const skipped = [];
|
|
5
|
+
const errors = [];
|
|
6
|
+
for (const [name, installer] of Object.entries(skillInstallers)) {
|
|
7
|
+
try {
|
|
8
|
+
const isInstalled = await installer.isInstalled(local);
|
|
9
|
+
if (isInstalled) {
|
|
10
|
+
await installer.update(local);
|
|
11
|
+
updated.push(name);
|
|
12
|
+
}
|
|
13
|
+
else {
|
|
14
|
+
skipped.push(name);
|
|
15
|
+
}
|
|
16
|
+
}
|
|
17
|
+
catch {
|
|
18
|
+
errors.push(name);
|
|
19
|
+
}
|
|
20
|
+
}
|
|
21
|
+
return { updated, skipped, errors };
|
|
22
|
+
}
|
|
23
|
+
//# sourceMappingURL=update-installed.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"update-installed.js","sourceRoot":"","sources":["../../../src/lib/skills/update-installed.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,eAAe,EAAE,MAAM,YAAY,CAAA;AAQ5C,MAAM,CAAC,KAAK,UAAU,wBAAwB,CAAC,KAAc;IACzD,MAAM,OAAO,GAAa,EAAE,CAAA;IAC5B,MAAM,OAAO,GAAa,EAAE,CAAA;IAC5B,MAAM,MAAM,GAAa,EAAE,CAAA;IAE3B,KAAK,MAAM,CAAC,IAAI,EAAE,SAAS,CAAC,IAAI,MAAM,CAAC,OAAO,CAAC,eAAe,CAAC,EAAE,CAAC;QAC9D,IAAI,CAAC;YACD,MAAM,WAAW,GAAG,MAAM,SAAS,CAAC,WAAW,CAAC,KAAK,CAAC,CAAA;YACtD,IAAI,WAAW,EAAE,CAAC;gBACd,MAAM,SAAS,CAAC,MAAM,CAAC,KAAK,CAAC,CAAA;gBAC7B,OAAO,CAAC,IAAI,CAAC,IAAI,CAAC,CAAA;YACtB,CAAC;iBAAM,CAAC;gBACJ,OAAO,CAAC,IAAI,CAAC,IAAI,CAAC,CAAA;YACtB,CAAC;QACL,CAAC;QAAC,MAAM,CAAC;YACL,MAAM,CAAC,IAAI,CAAC,IAAI,CAAC,CAAA;QACrB,CAAC;IACL,CAAC;IAED,OAAO,EAAE,OAAO,EAAE,OAAO,EAAE,MAAM,EAAE,CAAA;AACvC,CAAC"}
|
|
@@ -0,0 +1,2 @@
|
|
|
1
|
+
export type { SpinnerColor, SpinnerOptions } from '@todoist/cli-core';
|
|
2
|
+
export declare const LoadingSpinner: new () => import("@todoist/cli-core").LoadingSpinner, withSpinner: <T>(options: import("@todoist/cli-core").SpinnerOptions, op: () => Promise<T>) => Promise<T>, startEarlySpinner: (text?: string) => void, stopEarlySpinner: () => void;
|
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
import { createSpinner } from '@todoist/cli-core';
|
|
2
|
+
import { shouldDisableSpinner } from './global-args.js';
|
|
3
|
+
const spinner = createSpinner({ isDisabled: shouldDisableSpinner });
|
|
4
|
+
export const { LoadingSpinner, withSpinner, startEarlySpinner, stopEarlySpinner } = spinner;
|
|
5
|
+
//# sourceMappingURL=spinner.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"spinner.js","sourceRoot":"","sources":["../../src/lib/spinner.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,aAAa,EAAE,MAAM,mBAAmB,CAAA;AACjD,OAAO,EAAE,oBAAoB,EAAE,MAAM,kBAAkB,CAAA;AAIvD,MAAM,OAAO,GAAG,aAAa,CAAC,EAAE,UAAU,EAAE,oBAAoB,EAAE,CAAC,CAAA;AAEnE,MAAM,CAAC,MAAM,EAAE,cAAc,EAAE,WAAW,EAAE,iBAAiB,EAAE,gBAAgB,EAAE,GAAG,OAAO,CAAA"}
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
import type { AccountRef, UserRecord, UserRecordStore } from '@todoist/cli-core/auth';
|
|
2
|
+
import { type Config } from './config.js';
|
|
3
|
+
import { type OutlineAccount } from './outline-account.js';
|
|
4
|
+
/**
|
|
5
|
+
* `UserRecordStore` adapter over `config.users[]`. `StoredUser.token` is
|
|
6
|
+
* surfaced as `fallbackToken` — cli-core's name for the plaintext copy
|
|
7
|
+
* persisted only when the keyring is unreachable.
|
|
8
|
+
*/
|
|
9
|
+
export declare function createOutlineUserRecordStore(): UserRecordStore<OutlineAccount>;
|
|
10
|
+
/**
|
|
11
|
+
* Resolve the default-or-first `UserRecord` from an already-loaded config.
|
|
12
|
+
* Returns `null` when no users are stored.
|
|
13
|
+
*/
|
|
14
|
+
export declare function getDefaultUserRecord(config: Partial<Config>): UserRecord<OutlineAccount> | null;
|
|
15
|
+
/**
|
|
16
|
+
* Resolve a `UserRecord` by `--user` ref (UUID or display name). Falls back to
|
|
17
|
+
* the default-or-first record when `ref` is absent, so callers can pass the
|
|
18
|
+
* global selector straight through. Returns `null` when no users are stored or
|
|
19
|
+
* an explicit ref matches nothing.
|
|
20
|
+
*/
|
|
21
|
+
export declare function recordForRef(config: Partial<Config>, ref?: AccountRef): UserRecord<OutlineAccount> | null;
|
|
@@ -0,0 +1,126 @@
|
|
|
1
|
+
import { getConfig, updateConfig } from './config.js';
|
|
2
|
+
import { makeOutlineAccount, matchOutlineAccount } from './outline-account.js';
|
|
3
|
+
/**
|
|
4
|
+
* `UserRecordStore` adapter over `config.users[]`. `StoredUser.token` is
|
|
5
|
+
* surfaced as `fallbackToken` — cli-core's name for the plaintext copy
|
|
6
|
+
* persisted only when the keyring is unreachable.
|
|
7
|
+
*/
|
|
8
|
+
export function createOutlineUserRecordStore() {
|
|
9
|
+
return {
|
|
10
|
+
async list() {
|
|
11
|
+
const config = await getConfig();
|
|
12
|
+
return (config.users ?? []).map(toRecord);
|
|
13
|
+
},
|
|
14
|
+
async upsert(record) {
|
|
15
|
+
const config = await getConfig();
|
|
16
|
+
const existing = config.users ?? [];
|
|
17
|
+
const next = fromRecord(record);
|
|
18
|
+
const index = existing.findIndex((u) => u.id === record.account.id);
|
|
19
|
+
const users = index >= 0
|
|
20
|
+
? [...existing.slice(0, index), next, ...existing.slice(index + 1)]
|
|
21
|
+
: [...existing, next];
|
|
22
|
+
await updateConfig({ users });
|
|
23
|
+
},
|
|
24
|
+
async remove(id) {
|
|
25
|
+
const config = await getConfig();
|
|
26
|
+
const existing = config.users ?? [];
|
|
27
|
+
const index = existing.findIndex((u) => u.id === id);
|
|
28
|
+
if (index < 0)
|
|
29
|
+
return;
|
|
30
|
+
const users = [...existing.slice(0, index), ...existing.slice(index + 1)];
|
|
31
|
+
const updates = { users };
|
|
32
|
+
if (config.default_user_id === id)
|
|
33
|
+
updates.default_user_id = undefined;
|
|
34
|
+
await updateConfig(updates);
|
|
35
|
+
},
|
|
36
|
+
async getDefaultId() {
|
|
37
|
+
const config = await getConfig();
|
|
38
|
+
return config.default_user_id ?? null;
|
|
39
|
+
},
|
|
40
|
+
async setDefaultId(id) {
|
|
41
|
+
await updateConfig({ default_user_id: id ?? undefined });
|
|
42
|
+
},
|
|
43
|
+
};
|
|
44
|
+
}
|
|
45
|
+
/**
|
|
46
|
+
* Resolve the default-or-first `UserRecord` from an already-loaded config.
|
|
47
|
+
* Returns `null` when no users are stored.
|
|
48
|
+
*/
|
|
49
|
+
export function getDefaultUserRecord(config) {
|
|
50
|
+
const users = config.users ?? [];
|
|
51
|
+
if (users.length === 0)
|
|
52
|
+
return null;
|
|
53
|
+
const defaultId = config.default_user_id;
|
|
54
|
+
const user = (defaultId && users.find((u) => u.id === defaultId)) || users[0];
|
|
55
|
+
return toRecord(user);
|
|
56
|
+
}
|
|
57
|
+
/**
|
|
58
|
+
* Resolve a `UserRecord` by `--user` ref (UUID or display name). Falls back to
|
|
59
|
+
* the default-or-first record when `ref` is absent, so callers can pass the
|
|
60
|
+
* global selector straight through. Returns `null` when no users are stored or
|
|
61
|
+
* an explicit ref matches nothing.
|
|
62
|
+
*/
|
|
63
|
+
export function recordForRef(config, ref) {
|
|
64
|
+
if (ref === undefined)
|
|
65
|
+
return getDefaultUserRecord(config);
|
|
66
|
+
for (const user of config.users ?? []) {
|
|
67
|
+
const record = toRecord(user);
|
|
68
|
+
if (matchOutlineAccount(record.account, ref))
|
|
69
|
+
return record;
|
|
70
|
+
}
|
|
71
|
+
return null;
|
|
72
|
+
}
|
|
73
|
+
function toRecord(user) {
|
|
74
|
+
const account = makeOutlineAccount({
|
|
75
|
+
id: user.id,
|
|
76
|
+
label: user.name,
|
|
77
|
+
baseUrl: user.base_url,
|
|
78
|
+
oauthClientId: user.oauth_client_id,
|
|
79
|
+
teamName: user.team_name,
|
|
80
|
+
});
|
|
81
|
+
const record = { account };
|
|
82
|
+
const token = user.token?.trim();
|
|
83
|
+
if (token)
|
|
84
|
+
record.fallbackToken = token;
|
|
85
|
+
if (user.access_token_expires_at !== undefined) {
|
|
86
|
+
record.accessTokenExpiresAt = user.access_token_expires_at;
|
|
87
|
+
}
|
|
88
|
+
if (user.refresh_token_expires_at !== undefined) {
|
|
89
|
+
record.refreshTokenExpiresAt = user.refresh_token_expires_at;
|
|
90
|
+
}
|
|
91
|
+
if (user.has_refresh_token !== undefined)
|
|
92
|
+
record.hasRefreshToken = user.has_refresh_token;
|
|
93
|
+
return record;
|
|
94
|
+
}
|
|
95
|
+
function fromRecord(record) {
|
|
96
|
+
// Replace, don't merge: absent fields strip the corresponding slots so a
|
|
97
|
+
// stale value can't shadow a fresh keyring-backed write. cli-core contract.
|
|
98
|
+
const next = {
|
|
99
|
+
id: record.account.id,
|
|
100
|
+
name: record.account.label,
|
|
101
|
+
};
|
|
102
|
+
if (record.account.baseUrl)
|
|
103
|
+
next.base_url = record.account.baseUrl;
|
|
104
|
+
if (record.account.oauthClientId)
|
|
105
|
+
next.oauth_client_id = record.account.oauthClientId;
|
|
106
|
+
if (record.account.teamName)
|
|
107
|
+
next.team_name = record.account.teamName;
|
|
108
|
+
const token = record.fallbackToken?.trim();
|
|
109
|
+
if (token)
|
|
110
|
+
next.token = token;
|
|
111
|
+
// Deliberately NOT persisting `fallbackRefreshToken`: the refresh token is
|
|
112
|
+
// a long-lived credential and must stay in the secure store only. If the
|
|
113
|
+
// keyring is offline at write time, the refresh token isn't persisted, so
|
|
114
|
+
// that account fails closed (re-auth on next expiry) rather than leaving a
|
|
115
|
+
// long-lived secret in plaintext config. (Doist secrets-management standard.)
|
|
116
|
+
if (record.accessTokenExpiresAt !== undefined) {
|
|
117
|
+
next.access_token_expires_at = record.accessTokenExpiresAt;
|
|
118
|
+
}
|
|
119
|
+
if (record.refreshTokenExpiresAt !== undefined) {
|
|
120
|
+
next.refresh_token_expires_at = record.refreshTokenExpiresAt;
|
|
121
|
+
}
|
|
122
|
+
if (record.hasRefreshToken !== undefined)
|
|
123
|
+
next.has_refresh_token = record.hasRefreshToken;
|
|
124
|
+
return next;
|
|
125
|
+
}
|
|
126
|
+
//# sourceMappingURL=user-records.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"user-records.js","sourceRoot":"","sources":["../../src/lib/user-records.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
import type { OutlineTokenStore } from './auth-provider.js';
|
|
2
|
+
/**
|
|
3
|
+
* Bridge the global `ol --user <ref>` selector — stripped from argv before
|
|
4
|
+
* Commander parses (see `applyUserSelector`) — into store reads that make no
|
|
5
|
+
* explicit ref. An explicit `ref` argument always wins; only a missing one
|
|
6
|
+
* falls back to the global selector. Used by both the request path
|
|
7
|
+
* (`auth.ts` `tokenStore()`) and cli-core's auth attachers (`auth status` /
|
|
8
|
+
* `auth logout`), so a `--user` placed before any command is honoured
|
|
9
|
+
* everywhere.
|
|
10
|
+
*
|
|
11
|
+
* Existence is checked via `activeAccount(ref)` rather than `list()` so the
|
|
12
|
+
* check is both token-free (a keyring-offline account can still be cleared by
|
|
13
|
+
* `auth logout`) and legacy-aware (`list()` only exposes v2 records, but a
|
|
14
|
+
* pending v1→v2 migration can still resolve its single legacy account).
|
|
15
|
+
*/
|
|
16
|
+
export declare function withUserRefAware(store: OutlineTokenStore): OutlineTokenStore;
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
import { CliError } from './errors.js';
|
|
2
|
+
import { getRequestedUserRef } from './global-args.js';
|
|
3
|
+
/**
|
|
4
|
+
* Bridge the global `ol --user <ref>` selector — stripped from argv before
|
|
5
|
+
* Commander parses (see `applyUserSelector`) — into store reads that make no
|
|
6
|
+
* explicit ref. An explicit `ref` argument always wins; only a missing one
|
|
7
|
+
* falls back to the global selector. Used by both the request path
|
|
8
|
+
* (`auth.ts` `tokenStore()`) and cli-core's auth attachers (`auth status` /
|
|
9
|
+
* `auth logout`), so a `--user` placed before any command is honoured
|
|
10
|
+
* everywhere.
|
|
11
|
+
*
|
|
12
|
+
* Existence is checked via `activeAccount(ref)` rather than `list()` so the
|
|
13
|
+
* check is both token-free (a keyring-offline account can still be cleared by
|
|
14
|
+
* `auth logout`) and legacy-aware (`list()` only exposes v2 records, but a
|
|
15
|
+
* pending v1→v2 migration can still resolve its single legacy account).
|
|
16
|
+
*/
|
|
17
|
+
export function withUserRefAware(store) {
|
|
18
|
+
async function resolveRef(ref) {
|
|
19
|
+
const target = ref ?? getRequestedUserRef();
|
|
20
|
+
if (target === undefined)
|
|
21
|
+
return undefined;
|
|
22
|
+
if (!(await store.activeAccount(target))) {
|
|
23
|
+
throw new CliError('ACCOUNT_NOT_FOUND', `No stored account matches "${target}".`, [
|
|
24
|
+
'Check the account id or display name, or run `ol auth login` to add it.',
|
|
25
|
+
]);
|
|
26
|
+
}
|
|
27
|
+
return target;
|
|
28
|
+
}
|
|
29
|
+
return {
|
|
30
|
+
...store,
|
|
31
|
+
active: async (ref) => store.active(await resolveRef(ref)),
|
|
32
|
+
activeBundle: async (ref) => store.activeBundle(await resolveRef(ref)),
|
|
33
|
+
activeAccount: async (ref) => store.activeAccount(await resolveRef(ref)),
|
|
34
|
+
clear: async (ref) => store.clear(await resolveRef(ref)),
|
|
35
|
+
};
|
|
36
|
+
}
|
|
37
|
+
//# sourceMappingURL=user-ref-store.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"user-ref-store.js","sourceRoot":"","sources":["../../src/lib/user-ref-store.ts"],"names":[],"mappings":"AAEA,OAAO,EAAE,QAAQ,EAAE,MAAM,aAAa,CAAA;AACtC,OAAO,EAAE,mBAAmB,EAAE,MAAM,kBAAkB,CAAA;AAEtD;;;;;;;;;;;;;GAaG;AACH,MAAM,UAAU,gBAAgB,CAAC,KAAwB;IACrD,KAAK,UAAU,UAAU,CAAC,GAAgB;QACtC,MAAM,MAAM,GAAG,GAAG,IAAI,mBAAmB,EAAE,CAAA;QAC3C,IAAI,MAAM,KAAK,SAAS;YAAE,OAAO,SAAS,CAAA;QAC1C,IAAI,CAAC,CAAC,MAAM,KAAK,CAAC,aAAa,CAAC,MAAM,CAAC,CAAC,EAAE,CAAC;YACvC,MAAM,IAAI,QAAQ,CAAC,mBAAmB,EAAE,8BAA8B,MAAM,IAAI,EAAE;gBAC9E,yEAAyE;aAC5E,CAAC,CAAA;QACN,CAAC;QACD,OAAO,MAAM,CAAA;IACjB,CAAC;IAED,OAAO;QACH,GAAG,KAAK;QACR,MAAM,EAAE,KAAK,EAAE,GAAgB,EAAE,EAAE,CAAC,KAAK,CAAC,MAAM,CAAC,MAAM,UAAU,CAAC,GAAG,CAAC,CAAC;QACvE,YAAY,EAAE,KAAK,EAAE,GAAgB,EAAE,EAAE,CAAC,KAAK,CAAC,YAAY,CAAC,MAAM,UAAU,CAAC,GAAG,CAAC,CAAC;QACnF,aAAa,EAAE,KAAK,EAAE,GAAgB,EAAE,EAAE,CAAC,KAAK,CAAC,aAAa,CAAC,MAAM,UAAU,CAAC,GAAG,CAAC,CAAC;QACrF,KAAK,EAAE,KAAK,EAAE,GAAgB,EAAE,EAAE,CAAC,KAAK,CAAC,KAAK,CAAC,MAAM,UAAU,CAAC,GAAG,CAAC,CAAC;KACxE,CAAA;AACL,CAAC"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
import { rm } from 'node:fs/promises';
|
|
2
|
+
import { homedir } from 'node:os';
|
|
3
|
+
import { join } from 'node:path';
|
|
4
|
+
import { updateAllInstalledSkills } from './lib/skills/update-installed.js';
|
|
5
|
+
const AGENT_DIRS = ['.claude', '.codex', '.cursor', '.gemini', '.pi', '.agents'];
|
|
6
|
+
async function removeOldSkillDirs() {
|
|
7
|
+
const home = homedir();
|
|
8
|
+
await Promise.all(AGENT_DIRS.map((dir) => rm(join(home, dir, 'skills', 'outline'), { recursive: true, force: true })));
|
|
9
|
+
}
|
|
10
|
+
// TODO: Remove after a few releases once all users have upgraded.
|
|
11
|
+
// The skill was previously installed under "outline" but was renamed to "outline-cli"
|
|
12
|
+
// to match the convention used by the other Doist CLIs. This cleans up the old
|
|
13
|
+
// directories so users don't end up with duplicate skills.
|
|
14
|
+
removeOldSkillDirs().catch(() => { });
|
|
15
|
+
updateAllInstalledSkills(false).catch(() => { });
|
|
16
|
+
//# sourceMappingURL=postinstall.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"postinstall.js","sourceRoot":"","sources":["../src/postinstall.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,EAAE,EAAE,MAAM,kBAAkB,CAAA;AACrC,OAAO,EAAE,OAAO,EAAE,MAAM,SAAS,CAAA;AACjC,OAAO,EAAE,IAAI,EAAE,MAAM,WAAW,CAAA;AAChC,OAAO,EAAE,wBAAwB,EAAE,MAAM,kCAAkC,CAAA;AAE3E,MAAM,UAAU,GAAG,CAAC,SAAS,EAAE,QAAQ,EAAE,SAAS,EAAE,SAAS,EAAE,KAAK,EAAE,SAAS,CAAC,CAAA;AAEhF,KAAK,UAAU,kBAAkB;IAC7B,MAAM,IAAI,GAAG,OAAO,EAAE,CAAA;IACtB,MAAM,OAAO,CAAC,GAAG,CACb,UAAU,CAAC,GAAG,CAAC,CAAC,GAAG,EAAE,EAAE,CACnB,EAAE,CAAC,IAAI,CAAC,IAAI,EAAE,GAAG,EAAE,QAAQ,EAAE,SAAS,CAAC,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,KAAK,EAAE,IAAI,EAAE,CAAC,CAC7E,CACJ,CAAA;AACL,CAAC;AAED,kEAAkE;AAClE,sFAAsF;AACtF,+EAA+E;AAC/E,2DAA2D;AAC3D,kBAAkB,EAAE,CAAC,KAAK,CAAC,GAAG,EAAE,GAAE,CAAC,CAAC,CAAA;AAEpC,wBAAwB,CAAC,KAAK,CAAC,CAAC,KAAK,CAAC,GAAG,EAAE,GAAE,CAAC,CAAC,CAAA"}
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
interface RetryConfig {
|
|
2
|
+
retries: number;
|
|
3
|
+
retryCondition: (error: Error) => boolean;
|
|
4
|
+
retryDelay: (retryNumber: number) => number;
|
|
5
|
+
}
|
|
6
|
+
interface FetchOptions extends RequestInit {
|
|
7
|
+
timeout?: number;
|
|
8
|
+
}
|
|
9
|
+
interface FetchWithRetryArgs {
|
|
10
|
+
url: RequestInfo | URL;
|
|
11
|
+
options?: FetchOptions;
|
|
12
|
+
retryConfig?: Partial<RetryConfig>;
|
|
13
|
+
}
|
|
14
|
+
export declare function fetchWithRetry(args: FetchWithRetryArgs): Promise<Response>;
|
|
15
|
+
export {};
|
|
@@ -0,0 +1,144 @@
|
|
|
1
|
+
import { getDefaultTransport } from './http-dispatcher.js';
|
|
2
|
+
const DEFAULT_RETRY_CONFIG = {
|
|
3
|
+
retries: 0,
|
|
4
|
+
retryCondition: isNetworkError,
|
|
5
|
+
retryDelay: () => 0,
|
|
6
|
+
};
|
|
7
|
+
const TIMEOUT_ERROR_NAME = 'TimeoutError';
|
|
8
|
+
function createTimeoutError(timeoutMs) {
|
|
9
|
+
const error = new Error(`Request timeout after ${timeoutMs}ms`);
|
|
10
|
+
error.name = TIMEOUT_ERROR_NAME;
|
|
11
|
+
return error;
|
|
12
|
+
}
|
|
13
|
+
function isNetworkError(error) {
|
|
14
|
+
return error instanceof TypeError || error.name === TIMEOUT_ERROR_NAME;
|
|
15
|
+
}
|
|
16
|
+
function wait(delayMs) {
|
|
17
|
+
return new Promise((resolve) => setTimeout(resolve, delayMs));
|
|
18
|
+
}
|
|
19
|
+
function createTimeoutSignal(timeoutMs, existingSignal) {
|
|
20
|
+
const controller = new AbortController();
|
|
21
|
+
const timeoutId = setTimeout(() => {
|
|
22
|
+
controller.abort(createTimeoutError(timeoutMs));
|
|
23
|
+
}, timeoutMs);
|
|
24
|
+
let abortHandler;
|
|
25
|
+
function clear() {
|
|
26
|
+
clearTimeout(timeoutId);
|
|
27
|
+
if (existingSignal && abortHandler) {
|
|
28
|
+
existingSignal.removeEventListener('abort', abortHandler);
|
|
29
|
+
}
|
|
30
|
+
}
|
|
31
|
+
if (existingSignal) {
|
|
32
|
+
if (existingSignal.aborted) {
|
|
33
|
+
clearTimeout(timeoutId);
|
|
34
|
+
controller.abort(existingSignal.reason);
|
|
35
|
+
}
|
|
36
|
+
else {
|
|
37
|
+
abortHandler = () => {
|
|
38
|
+
clearTimeout(timeoutId);
|
|
39
|
+
controller.abort(existingSignal.reason);
|
|
40
|
+
};
|
|
41
|
+
existingSignal.addEventListener('abort', abortHandler, { once: true });
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
controller.signal.addEventListener('abort', () => {
|
|
45
|
+
clearTimeout(timeoutId);
|
|
46
|
+
});
|
|
47
|
+
return { signal: controller.signal, clear };
|
|
48
|
+
}
|
|
49
|
+
/**
|
|
50
|
+
* Both `Request` classes in play here — the global one and undici's — reject
|
|
51
|
+
* each other's instances: the check is a plain `instanceof`, and anything that
|
|
52
|
+
* fails it gets stringified, so the request dies with `Failed to parse URL
|
|
53
|
+
* from [object Request]`. Duck-typing catches either class.
|
|
54
|
+
*/
|
|
55
|
+
function isRequestLike(value) {
|
|
56
|
+
return (typeof value === 'object' &&
|
|
57
|
+
value !== null &&
|
|
58
|
+
'url' in value &&
|
|
59
|
+
'method' in value &&
|
|
60
|
+
'headers' in value);
|
|
61
|
+
}
|
|
62
|
+
/**
|
|
63
|
+
* Flatten a `Request` input into a plain URL and init, so neither `fetch`
|
|
64
|
+
* implementation is ever handed a `Request` the other one owns.
|
|
65
|
+
* `fetchWithRetry` is exposed as a `typeof fetch` (see `outlineFetch` in
|
|
66
|
+
* `auth-provider.ts`), so a `Request` is a shape callers may legitimately pass.
|
|
67
|
+
*
|
|
68
|
+
* Reading the body to a buffer up front also keeps it replayable: a request
|
|
69
|
+
* stream is consumed by the first attempt and would replay as an empty body.
|
|
70
|
+
*
|
|
71
|
+
* Explicit `options` win over the request's own fields, matching
|
|
72
|
+
* `fetch(request, init)`.
|
|
73
|
+
*/
|
|
74
|
+
async function flattenRequestInput(url, options) {
|
|
75
|
+
if (typeof url === 'string' || url instanceof URL || !isRequestLike(url)) {
|
|
76
|
+
return { url: url, options };
|
|
77
|
+
}
|
|
78
|
+
const body = url.body ? await url.arrayBuffer() : undefined;
|
|
79
|
+
return {
|
|
80
|
+
url: url.url,
|
|
81
|
+
options: {
|
|
82
|
+
method: url.method,
|
|
83
|
+
headers: [...url.headers],
|
|
84
|
+
...(body === undefined ? {} : { body }),
|
|
85
|
+
...(url.signal ? { signal: url.signal } : {}),
|
|
86
|
+
...options,
|
|
87
|
+
},
|
|
88
|
+
};
|
|
89
|
+
}
|
|
90
|
+
export async function fetchWithRetry(args) {
|
|
91
|
+
const { url, options: rawOptions = {}, retryConfig = {} } = args;
|
|
92
|
+
const { url: requestUrl, options } = await flattenRequestInput(url, rawOptions);
|
|
93
|
+
const config = { ...DEFAULT_RETRY_CONFIG, ...retryConfig };
|
|
94
|
+
const { timeout, signal: userSignal, ...requestOptions } = options;
|
|
95
|
+
let lastError;
|
|
96
|
+
for (let attempt = 0; attempt <= config.retries; attempt++) {
|
|
97
|
+
let clearTimeoutFn;
|
|
98
|
+
try {
|
|
99
|
+
let requestSignal = userSignal ?? undefined;
|
|
100
|
+
if (timeout && timeout > 0) {
|
|
101
|
+
const timeoutResult = createTimeoutSignal(timeout, requestSignal);
|
|
102
|
+
requestSignal = timeoutResult.signal;
|
|
103
|
+
clearTimeoutFn = timeoutResult.clear;
|
|
104
|
+
}
|
|
105
|
+
const fetchOptions = {
|
|
106
|
+
...requestOptions,
|
|
107
|
+
signal: requestSignal,
|
|
108
|
+
};
|
|
109
|
+
// Take the dispatcher and its `fetch` as one value: the dispatcher
|
|
110
|
+
// decompresses the body itself, so a `fetch` from a different
|
|
111
|
+
// undici build decodes it a second time and the request fails with
|
|
112
|
+
// `terminated`. A `fetch` of `undefined` means the global one is
|
|
113
|
+
// the right partner (see `DefaultTransport`).
|
|
114
|
+
const transport = getDefaultTransport();
|
|
115
|
+
// @ts-expect-error dispatcher is supported by Node.js fetch via Undici
|
|
116
|
+
fetchOptions.dispatcher = transport.dispatcher;
|
|
117
|
+
// undici's `fetch` and the global `fetch` have the same call shape;
|
|
118
|
+
// the cast only reconciles undici's own Request/Response types with
|
|
119
|
+
// the global lib types.
|
|
120
|
+
const fetchImpl = (transport.fetch ?? fetch);
|
|
121
|
+
const response = await fetchImpl(requestUrl, fetchOptions);
|
|
122
|
+
if (clearTimeoutFn) {
|
|
123
|
+
clearTimeoutFn();
|
|
124
|
+
}
|
|
125
|
+
return response;
|
|
126
|
+
}
|
|
127
|
+
catch (error) {
|
|
128
|
+
if (clearTimeoutFn) {
|
|
129
|
+
clearTimeoutFn();
|
|
130
|
+
}
|
|
131
|
+
lastError = error;
|
|
132
|
+
const shouldRetry = attempt < config.retries && config.retryCondition(lastError);
|
|
133
|
+
if (!shouldRetry) {
|
|
134
|
+
throw lastError;
|
|
135
|
+
}
|
|
136
|
+
const delay = config.retryDelay(attempt + 1);
|
|
137
|
+
if (delay > 0) {
|
|
138
|
+
await wait(delay);
|
|
139
|
+
}
|
|
140
|
+
}
|
|
141
|
+
}
|
|
142
|
+
throw lastError ?? new Error('Request failed after retries');
|
|
143
|
+
}
|
|
144
|
+
//# sourceMappingURL=fetch-with-retry.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"fetch-with-retry.js","sourceRoot":"","sources":["../../src/transport/fetch-with-retry.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
import { type Dispatcher, fetch as undiciFetch } from 'undici';
|
|
2
|
+
type UndiciFetch = typeof undiciFetch;
|
|
3
|
+
/**
|
|
4
|
+
* A dispatcher and the `fetch` it must be used with. `fetch` is undici's own
|
|
5
|
+
* `fetch` when the decompress interceptor is composed, or `undefined` (meaning:
|
|
6
|
+
* use the runtime's global `fetch`) on runtimes that ship a partial undici.
|
|
7
|
+
*
|
|
8
|
+
* The two halves are cached together so a caller can never hold a dispatcher
|
|
9
|
+
* paired with a mismatched `fetch`. The dispatcher decompresses the response
|
|
10
|
+
* body itself and strips `content-encoding` from the parsed headers only; a
|
|
11
|
+
* `fetch` from a different undici build reads the *raw* headers, still sees
|
|
12
|
+
* `content-encoding: gzip`, and decodes an already-decoded body — the request
|
|
13
|
+
* then dies mid-stream with `TypeError: terminated`. Node's global `fetch` is
|
|
14
|
+
* backed by whatever undici ships inside that Node release (6.x on Node 22 …
|
|
15
|
+
* 8.x on Node 26), which is never the npm `undici` this dispatcher comes from,
|
|
16
|
+
* so sourcing `fetch` from the same package keeps the whole request path on
|
|
17
|
+
* one version.
|
|
18
|
+
*/
|
|
19
|
+
export type DefaultTransport = {
|
|
20
|
+
dispatcher: Dispatcher;
|
|
21
|
+
fetch: UndiciFetch | undefined;
|
|
22
|
+
};
|
|
23
|
+
/**
|
|
24
|
+
* The default dispatcher and the `fetch` it must be used with, as a single
|
|
25
|
+
* value so the two are always read together. Use both halves:
|
|
26
|
+
*
|
|
27
|
+
* ```ts
|
|
28
|
+
* const transport = getDefaultTransport()
|
|
29
|
+
* const fetchImpl = transport.fetch ?? fetch
|
|
30
|
+
* await fetchImpl(url, { ...options, dispatcher: transport.dispatcher })
|
|
31
|
+
* ```
|
|
32
|
+
*/
|
|
33
|
+
export declare function getDefaultTransport(): DefaultTransport;
|
|
34
|
+
export declare function resetDefaultDispatcherForTests(): Promise<void>;
|
|
35
|
+
type SyncOnly<T> = T extends PromiseLike<unknown> ? never : T;
|
|
36
|
+
export declare function suppressExperimentalWarningsSync<T>(fn: () => SyncOnly<T>): SyncOnly<T>;
|
|
37
|
+
export {};
|
|
@@ -0,0 +1,95 @@
|
|
|
1
|
+
import { Agent, EnvHttpProxyAgent, fetch as undiciFetch, interceptors, } from 'undici';
|
|
2
|
+
const KEEP_ALIVE_OPTIONS = {
|
|
3
|
+
keepAliveTimeout: 1,
|
|
4
|
+
keepAliveMaxTimeout: 1,
|
|
5
|
+
};
|
|
6
|
+
const PROXY_ENV_KEYS = ['HTTPS_PROXY', 'https_proxy', 'HTTP_PROXY', 'http_proxy'];
|
|
7
|
+
let defaultTransport;
|
|
8
|
+
function hasProxyEnv() {
|
|
9
|
+
for (const key of PROXY_ENV_KEYS) {
|
|
10
|
+
if (process.env[key]) {
|
|
11
|
+
return true;
|
|
12
|
+
}
|
|
13
|
+
}
|
|
14
|
+
return false;
|
|
15
|
+
}
|
|
16
|
+
function createDefaultTransport() {
|
|
17
|
+
const base = hasProxyEnv()
|
|
18
|
+
? new EnvHttpProxyAgent(KEEP_ALIVE_OPTIONS)
|
|
19
|
+
: new Agent(KEEP_ALIVE_OPTIONS);
|
|
20
|
+
// Some runtimes report `process.versions.node` but ship only a partial
|
|
21
|
+
// undici: `interceptors.decompress` is absent and dispatchers have no
|
|
22
|
+
// `.compose`. Bun is the common case. There the base agent alone is
|
|
23
|
+
// enough — Bun's `fetch` decompresses gzip/deflate/br/zstd natively — so
|
|
24
|
+
// skip the interceptor instead of crashing on the missing API. Optional
|
|
25
|
+
// chaining also guards a runtime that omits the `interceptors` export.
|
|
26
|
+
if (typeof interceptors?.decompress !== 'function') {
|
|
27
|
+
// Nothing decompresses on our side, so the global `fetch` — which
|
|
28
|
+
// decompresses natively here — is the correct partner.
|
|
29
|
+
return { dispatcher: base, fetch: undefined };
|
|
30
|
+
}
|
|
31
|
+
// Compose the response-decompression interceptor so gzip/deflate/br/zstd
|
|
32
|
+
// bodies are decoded before consumers parse them. Required on Node 24+:
|
|
33
|
+
// attaching any custom dispatcher to the global `fetch` strips the
|
|
34
|
+
// `content-encoding` header but does not actually decompress the body,
|
|
35
|
+
// so callers receive raw gzipped bytes and `JSON.parse` fails.
|
|
36
|
+
// See https://github.com/todoist/todoist-cli/issues/318.
|
|
37
|
+
const decompress = suppressExperimentalWarningsSync(() => interceptors.decompress());
|
|
38
|
+
return { dispatcher: base.compose(decompress), fetch: undiciFetch };
|
|
39
|
+
}
|
|
40
|
+
/**
|
|
41
|
+
* The default dispatcher and the `fetch` it must be used with, as a single
|
|
42
|
+
* value so the two are always read together. Use both halves:
|
|
43
|
+
*
|
|
44
|
+
* ```ts
|
|
45
|
+
* const transport = getDefaultTransport()
|
|
46
|
+
* const fetchImpl = transport.fetch ?? fetch
|
|
47
|
+
* await fetchImpl(url, { ...options, dispatcher: transport.dispatcher })
|
|
48
|
+
* ```
|
|
49
|
+
*/
|
|
50
|
+
export function getDefaultTransport() {
|
|
51
|
+
defaultTransport ??= createDefaultTransport();
|
|
52
|
+
return defaultTransport;
|
|
53
|
+
}
|
|
54
|
+
export async function resetDefaultDispatcherForTests() {
|
|
55
|
+
if (!defaultTransport) {
|
|
56
|
+
return;
|
|
57
|
+
}
|
|
58
|
+
const { dispatcher } = defaultTransport;
|
|
59
|
+
defaultTransport = undefined;
|
|
60
|
+
await dispatcher.close();
|
|
61
|
+
}
|
|
62
|
+
export function suppressExperimentalWarningsSync(fn) {
|
|
63
|
+
const originalEmit = process.emitWarning;
|
|
64
|
+
const filteredEmit = ((...args) => {
|
|
65
|
+
const typeOrOptions = args[1];
|
|
66
|
+
const type = typeof typeOrOptions === 'string'
|
|
67
|
+
? typeOrOptions
|
|
68
|
+
: typeof typeOrOptions === 'object' &&
|
|
69
|
+
typeOrOptions !== null &&
|
|
70
|
+
'type' in typeOrOptions
|
|
71
|
+
? typeOrOptions.type
|
|
72
|
+
: undefined;
|
|
73
|
+
if (type === 'ExperimentalWarning')
|
|
74
|
+
return;
|
|
75
|
+
Reflect.apply(originalEmit, process, args);
|
|
76
|
+
});
|
|
77
|
+
process.emitWarning = filteredEmit;
|
|
78
|
+
try {
|
|
79
|
+
const result = fn();
|
|
80
|
+
if (isThenable(result)) {
|
|
81
|
+
throw new Error('suppressExperimentalWarningsSync: callback returned a thenable; this helper only supports synchronous work.');
|
|
82
|
+
}
|
|
83
|
+
return result;
|
|
84
|
+
}
|
|
85
|
+
finally {
|
|
86
|
+
process.emitWarning = originalEmit;
|
|
87
|
+
}
|
|
88
|
+
}
|
|
89
|
+
function isThenable(value) {
|
|
90
|
+
return (typeof value === 'object' &&
|
|
91
|
+
value !== null &&
|
|
92
|
+
'then' in value &&
|
|
93
|
+
typeof value.then === 'function');
|
|
94
|
+
}
|
|
95
|
+
//# sourceMappingURL=http-dispatcher.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"http-dispatcher.js","sourceRoot":"","sources":["../../src/transport/http-dispatcher.ts"],"names":[],"mappings":"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"}
|