incur 0.1.17 → 0.2.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/src/e2e.test.ts CHANGED
@@ -1,4 +1,5 @@
1
1
  import { Cli, Errors, Skill, Typegen, z } from 'incur'
2
+ import { app as honoApp } from '../test/fixtures/hono-api.js'
2
3
 
3
4
  let __mockSkillsHash: string | undefined
4
5
 
@@ -821,6 +822,7 @@ describe('help', () => {
821
822
  Usage: app <command>
822
823
 
823
824
  Commands:
825
+ api Proxy to HTTP API
824
826
  auth Authentication commands
825
827
  config Show current configuration
826
828
  echo Echo back arguments
@@ -980,6 +982,7 @@ describe('--llms', () => {
980
982
  const names = manifest.commands.map((c: any) => c.name)
981
983
  expect(names).toMatchInlineSnapshot(`
982
984
  [
985
+ "api",
983
986
  "auth login",
984
987
  "auth logout",
985
988
  "auth status",
@@ -1180,6 +1183,7 @@ describe('typegen', () => {
1180
1183
  "declare module 'incur' {
1181
1184
  interface Register {
1182
1185
  commands: {
1186
+ 'api': { args: {}; options: {} }
1183
1187
  'auth login': { args: {}; options: { hostname: string; web: boolean; scopes: string[] } }
1184
1188
  'auth logout': { args: {}; options: {} }
1185
1189
  'auth status': { args: {}; options: {} }
@@ -1764,6 +1768,176 @@ describe('deprecated flags', () => {
1764
1768
  })
1765
1769
  })
1766
1770
 
1771
+ describe('fetch gateway', () => {
1772
+ test('routes to fetch handler', async () => {
1773
+ const { output } = await serve(createApp(), ['api', 'health'])
1774
+ expect(output).toMatchInlineSnapshot(`
1775
+ "ok: true
1776
+ "
1777
+ `)
1778
+ })
1779
+
1780
+ test('path segments map to URL path', async () => {
1781
+ const { output } = await serve(createApp(), ['api', 'users'])
1782
+ expect(output).toContain('Alice')
1783
+ })
1784
+
1785
+ test('path segments with dynamic params', async () => {
1786
+ const { output } = await serve(createApp(), ['api', 'users', '42'])
1787
+ expect(output).toMatchInlineSnapshot(`
1788
+ "id: 42
1789
+ name: Alice
1790
+ "
1791
+ `)
1792
+ })
1793
+
1794
+ test('query params from --key value', async () => {
1795
+ const { output } = await serve(createApp(), ['api', 'users', '--limit', '5'])
1796
+ const { output: jsonOut } = await serve(createApp(), [
1797
+ 'api',
1798
+ 'users',
1799
+ '--limit',
1800
+ '5',
1801
+ '--format',
1802
+ 'json',
1803
+ ])
1804
+ expect(json(jsonOut).limit).toBe(5)
1805
+ })
1806
+
1807
+ test('POST with -X and -d', async () => {
1808
+ const { output } = await serve(createApp(), [
1809
+ 'api',
1810
+ 'users',
1811
+ '-X',
1812
+ 'POST',
1813
+ '-d',
1814
+ '{"name":"Bob"}',
1815
+ ])
1816
+ expect(output).toMatchInlineSnapshot(`
1817
+ "created: true
1818
+ name: Bob
1819
+ "
1820
+ `)
1821
+ })
1822
+
1823
+ test('implicit POST with --body', async () => {
1824
+ const { output } = await serve(createApp(), [
1825
+ 'api',
1826
+ 'users',
1827
+ '--body',
1828
+ '{"name":"Eve"}',
1829
+ ])
1830
+ expect(output).toMatchInlineSnapshot(`
1831
+ "created: true
1832
+ name: Eve
1833
+ "
1834
+ `)
1835
+ })
1836
+
1837
+ test('DELETE with --method', async () => {
1838
+ const { output } = await serve(createApp(), [
1839
+ 'api',
1840
+ 'users',
1841
+ '1',
1842
+ '--method',
1843
+ 'DELETE',
1844
+ ])
1845
+ expect(output).toMatchInlineSnapshot(`
1846
+ "deleted: true
1847
+ id: 1
1848
+ "
1849
+ `)
1850
+ })
1851
+
1852
+ test('error response produces error envelope', async () => {
1853
+ const { output, exitCode } = await serve(createApp(), ['api', 'error'])
1854
+ expect(exitCode).toBe(1)
1855
+ expect(output).toMatchInlineSnapshot(`
1856
+ "code: HTTP_404
1857
+ message: not found
1858
+ "
1859
+ `)
1860
+ })
1861
+
1862
+ test('text response', async () => {
1863
+ const { output } = await serve(createApp(), ['api', 'text'])
1864
+ expect(output).toMatchInlineSnapshot(`
1865
+ "hello world
1866
+ "
1867
+ `)
1868
+ })
1869
+
1870
+ test('--format json', async () => {
1871
+ const { output } = await serve(createApp(), ['api', 'health', '--format', 'json'])
1872
+ expect(json(output)).toEqual({ ok: true })
1873
+ })
1874
+
1875
+ test('--verbose wraps in envelope', async () => {
1876
+ const { output } = await serve(createApp(), [
1877
+ 'api',
1878
+ 'health',
1879
+ '--verbose',
1880
+ '--format',
1881
+ 'json',
1882
+ ])
1883
+ const parsed = json(output)
1884
+ expect(parsed.ok).toBe(true)
1885
+ expect(parsed.data).toEqual({ ok: true })
1886
+ expect(parsed.meta.command).toBe('api')
1887
+ expect(parsed.meta.duration).toBeDefined()
1888
+ })
1889
+
1890
+ test('--help shows curl-style flags', async () => {
1891
+ const { output } = await serve(createApp(), ['api', '--help'])
1892
+ expect(output).toContain('Proxy to HTTP API')
1893
+ expect(output).toContain('--method')
1894
+ expect(output).toContain('--header')
1895
+ expect(output).toContain('--body')
1896
+ expect(output).toContain('--data')
1897
+ })
1898
+
1899
+ test('appears in root --help', async () => {
1900
+ const { output } = await serve(createApp(), ['--help'])
1901
+ expect(output).toContain('api')
1902
+ expect(output).toContain('Proxy to HTTP API')
1903
+ })
1904
+
1905
+ test('appears in --llms', async () => {
1906
+ const { output } = await serve(createApp(), ['--llms'])
1907
+ expect(output).toContain('api')
1908
+ expect(output).toContain('Proxy to HTTP API')
1909
+ })
1910
+
1911
+ test('coexists with native commands', async () => {
1912
+ const { output: fetchOut } = await serve(createApp(), ['api', 'health'])
1913
+ expect(fetchOut).toContain('ok: true')
1914
+ const { output: nativeOut } = await serve(createApp(), ['ping'])
1915
+ expect(nativeOut).toContain('pong: true')
1916
+ })
1917
+
1918
+ test('streaming NDJSON response', async () => {
1919
+ const { output } = await serve(createApp(), ['api', 'stream'])
1920
+ expect(output).toMatchInlineSnapshot(`
1921
+ "progress: 1
1922
+ progress: 2
1923
+ "
1924
+ `)
1925
+ })
1926
+
1927
+ test('streaming NDJSON --format json buffers all chunks', async () => {
1928
+ const { output } = await serve(createApp(), ['api', 'stream', '--format', 'json'])
1929
+ expect(json(output)).toEqual([{ progress: 1 }, { progress: 2 }])
1930
+ })
1931
+
1932
+ test('streaming NDJSON --format jsonl', async () => {
1933
+ const { output } = await serve(createApp(), ['api', 'stream', '--format', 'jsonl'])
1934
+ const lines = output.trim().split('\n').map((l) => JSON.parse(l))
1935
+ expect(lines[0]).toEqual({ type: 'chunk', data: { progress: 1 } })
1936
+ expect(lines[1]).toEqual({ type: 'chunk', data: { progress: 2 } })
1937
+ expect(lines[2].type).toBe('done')
1938
+ })
1939
+ })
1940
+
1767
1941
  async function serve(
1768
1942
  cli: { serve: Cli.Cli['serve'] },
1769
1943
  argv: string[],
@@ -2174,6 +2348,7 @@ function createApp() {
2174
2348
  cli.command(auth)
2175
2349
  cli.command(project)
2176
2350
  cli.command(config)
2351
+ cli.command('api', { description: 'Proxy to HTTP API', fetch: honoApp.fetch })
2177
2352
 
2178
2353
  return cli
2179
2354
  }
package/src/index.ts CHANGED
@@ -7,6 +7,8 @@ export * as Errors from './Errors.js'
7
7
  export * as Formatter from './Formatter.js'
8
8
  export * as Help from './Help.js'
9
9
  export * as Mcp from './Mcp.js'
10
+ export * as Fetch from './Fetch.js'
11
+ export * as Openapi from './Openapi.js'
10
12
  export * as Parser from './Parser.js'
11
13
  export type { Register } from './Register.js'
12
14
  export * as Schema from './Schema.js'